A Product can have various sizes so I added one to many relation. But Each product can have only 1 unique size, i.e, in many to many the sizes should not repeat.
For instance, Product X can have size 1, 2, 3 but if user tries to add size 2 which already exists, it should fail (and it does) but now the issue is when Product Y is added, adding size 1 fails because it was unique but It should be unique per variant not overall. Any way to do this while modelling the DB or I have to add a manual check and throw error if the size of the same variant already exists.
Database - Postgresql
Using Prisma
In Prisma, you can model your relations like this:
model Product {
id String #id #default(uuid())
name String
description String
sizes ProductSize[]
}
model ProductSize {
id String #id #default(uuid())
name String
description String
product Product #relation(fields: [productId], references: [id])
productId String
##unique([productId, name])
}
This contains a unique index over a combination of two fields (productId and name). So for each product there can only be a unique named size.
Example - (productId - A, Size - 1), (productId - A, Size - 2). Adding another record with (productId - A, Size - 1) would throw an error but (productId - B, Size - 1) will be allowed.
You can create unique indexes using multiple fields in postgres. https://www.postgresqltutorial.com/postgresql-indexes/postgresql-unique-index/ scroll down a bit for the multiple fields section.
Without knowing your existing table structure I can't say with confidence that this code would work but you'll want something like this.
CREATE UNIQUE INDEX idx_products_size ON products(id, size);
Related
I'm starting to learn Odoo and I'm developing an application in Odoo 12. I want to represent a database with intermediate table, in this case I have 2 tables ( products and actions ) and the relationship is 1...n and I must have an intermediate table that joins two tables.
Product (id, name, price...)
Action (id, name)
Product_action (id, id_product, id_action, date_from, date_to)
How I can do this? How can i represent this in the Odoo's model?
Thanks in advance.
Just create a model for the intermediate. You can also represent the entries of this intermediate model (rows in table) as 1:n relation on both the other models:
class ProductAction(models.Model):
_name = "product.action"
date_from = fields.Date()
date_to = fields.Date()
product_id = fields.Many2one(comodel_name="product.product")
action_id = fields.Many2one(comodel_name="action.model.name")
class ProductProduct(models.Model):
_inherit = "product.product"
intermediate_ids = fields.One2many(
comodel_name="product.action", inverse_name="product_id")
class ActionClassName(models.Model):
_inherit = "action.model.name"
intermediate_ids = fields.One2many(
comodel_name="product.action", inverse_name="action_id")
I assumed you are using Odoo's product class product.product but don't know what class/model is used for "actions" so i used a wildcard name for it in my example code.
I am making a project where (from the perspective of school) you can calculate each student average.
You can register a student (first entity) on a screen and subjects (second entity) on another screen.
Student has name, email, grade and average as atributes, and Subjects has name. They relate many-to-many with each other.
I am trying to create a copy of subjects list to each student, then on each student i can register a grade to each subject. Like this:
Model concept
Model:
!https://imgur.com/gmXyR5j
I've created a singleton of subjects since it is used more than one location:
import Foundation
import CoreData
class SubjectsManager {
static let shared = SubjectsManager()
var subjects: [Subject] = []
func loadSubject(with context: NSManagedObjectContext) {
let fetchRequest: NSFetchRequest<Subject> = Subject.fetchRequest()
let sortDescritor = NSSortDescriptor(key: "name", ascending: true)
fetchRequest.sortDescriptors = [sortDescritor]
do {
subjects = try context.fetch(fetchRequest)
} catch {
print(error.localizedDescription)
}
}
func deleteSubject(index: Int, context: NSManagedObjectContext) {
let subject = subjects[index]
context.delete(subject)
do {
try context.save()
subjects.remove(at: index)
} catch {
print(error.localizedDescription)
}
}
private init() {
}
}
And, on my student screen, i've tried many thing but nothing is working.
The to-many relation of student with subject is called registeredSubjects
I've created a NSSET called subjectsManagerSet to get values from the singleton, but it not working. Here what i've tried so far:
subjectManagerSet.addingObjects(from: subjectsManager.subjects)
Also tried to create a for loop of subjectManager.subjects to add on subjectManagerSet but it's not working too.
About errors, when i get samples from xcode output, it keep showing that subjectManagerSet did not get values from subjectManager.subject
Error message:
2019-09-26 20:38:16.983725-0300 MyAverage[1734:62290] Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value: file /Users/vitorgomes/Desktop/Mentorizacao/MyAverage/MyAverage/Controllers/StudentsViewController.swift, line 119
(lldb) po subjectManagerSet
0 elements
The expected result is that i want a copy of subjects for each student instance, then i can add grades for each subjects for each student.
The expected result is that i want a copy of subjects for each student
instance, then i can add grades for each subjects for each student.
I am not addressing your problem as stated at the beginning of your question, but the one stated at the end, per the quote above.
I would encourage you to reconsider the structure of your model.
Perhaps something like this?
In this proposed model, you're able to assign to an object of the entity Enrolment:
a grade (and date) via these attribute properties;
a student via the one-to-many relationship property with a Student entity;
a subject via the one-to-many relationship property with a Subject entity.
In the following examples, I assume Core Data generated NSManagedObject subclasses - that is - in the Data Model Inspector, set the value for Codegen = Class Definition (default).
(Personally and as an aside, I prefer to manually write NSManagedObject subclasses for each of my entities and use Set rather than the NSSet, as I find it subsequently a lot easier to maintain type integrity in my code. But I have not done that here as most people who are new to Core Data will use the default for Codegen noted above.)
You're able to access these values in the following manner...
let student = Student()
print("Student name is: \(String(describing: student.name))")
if let studentEnrolments: NSSet = student.studentEnrolments {
for item in studentEnrolments {
if
let enrolment = item as? Enrolment,
let subjectName: String = enrolment.subject?.name {
print("Subject name for this student is: \(subjectName)")
}
}
}
Its easy to assign a subject enrolment to a student...
let enrolment = Enrolment()
let subject = Subject()
enrolment.subject = subject
student.addToStudentEnrolments(enrolment)
Then now or later, a grade can be applied to the enrolled subject...
let grade: String = "A"
enrolment.grade = grade
Of course the average becomes a mathematical function based on the sum of all grades for each student, divided by the count. This is in my humble opinion, better constructed as it is needed, rather than saved as an attribute with each Student object.
Update
Im updating my answer to include a little database theory to explain my proposed object model.
According to Wikipedia, Database normalisation is...
the process of structuring a relational database
in accordance with a series of so-called normal forms in order to
reduce data redundancy and improve data integrity.
What does this practically mean to me? It means breaking my data down into its most discrete and unique parts, so that, in theory, I never need to enter any unique piece of data more than once.
Let me use a simple table example as a means of explaining this, as it might be set out in a spreadsheet (or your model concept):
Original Data
TABLE 1
A B C
1 STUDENT SUBJECT GRADE
2 Student1 Mathematics 8.8
3 Student1 Physics 7.0
4 Student1 Biology 6.0
5 Student2 Mathematics 5.0
6 Student2 Physics 9.0
7 Student2 Biology 7.0
Normalised Data
TABLE 1 TABLE 2 TABLE 3
A B C A B A B
1 STUDENT SUBJECT GRADE ID STUDENT ID SUBJECT
2 1 1 8.8 1 Student1 1 Mathematics
3 1 2 7.0 2 Student2 2 Physics
4 1 3 6.0 3 Biology
5 2 1 5.0
6 2 2 9.0
7 2 3 7.0
The normalised data uses relationships between the three tables. It stores the ID (as a primary key) of each STUDENT and each SUBJECT, instead of the actual words. This is obviously far more efficient in many different ways, including but not limited to: bytes of stored data, ability to index, speed of data retrieval.
When you set a relationship property in your Core Data object model graph, you are doing the same thing...
So for your example, the Core Data object model graph Entity replaces TABLE. The Core Data framework automagically inserts a primary key column into the SQLite database for us when it constructs the tables and later a primary key unique integer when we programmatically add rows (records, a.k.a instances of an entity). While we don't have direct access to that as a developer (using Core Data), the Core Data framework allows us to build one-to-one, one-to-many and many-to-many relationships between two entities, that achieves the same outcome.
Enrolment Student Subject
A B C A B A B
Rel. Rel. Att. Rel. Att. Rel. Att.
∞ ∞ 1 1
Z_PK Student Subject grade Z_PK name Z_PK name
1 1 1 8.8 1 Student1 1 Mathematics
2 1 2 7.0 2 Student2 2 Physics
3 1 3 6.0 3 Biology
4 2 1 5.0
5 2 2 9.0
6 2 3 7.0
Att. = Entity Attribute;
Rel. = Entity Relationship;
∞ = many side of one-to-many Relationship (<<-);
1 = one side of one-to-many Relationship (-->)
Any questions, let me know?
I have to insert data to db in form fields that have the same path and i want to save it different id's but rather it concatenated it with ",", how could i possibly do it?
I tried to make some alias in SQL but it saves into same db field name with concatenated with ","
i expected in db when i insert that
EX.
db field name = description
input 1 value = "john";
input 2 value = "doe";
id description
1 john
2 doe
above is my expected result
but in my case when i insert it shows these
id description
1 john,doe
can someone help me to achieve that result ? THANKYOU!
Let me present a similar situation. You have a database of people and you are concerned that each person might have multiple phone numbers.
CREATE TABLE Persons (
person_id INT UNSIGNED AUTO_INCREMENT,
...
PRIMARY KEY(person_id) );
CREATE TABLE PhoneNumbers (
person_id INT UNSIGNED,
phone VARCHAR(20) CHARACTER SET ascii,
type ENUM('unknown', 'cell', 'home', 'work'),
PRIMARY KEY(person_id, phone) );
The table PhoneNumbers has a "many-to-1" relationship between phone numbers and persons. (It does not care if two persons share the same number.)
SELECT ...
GROUP CONCAT(pn.phone) AS phone_numbers,
...
FROM Persons AS p
LEFT JOIN PhoneNumbers AS pn USING(person_id)
...;
will deliver a commalist of phone numbers (eg: 123-456-7890,333-444-5555) for each person being selected. Because of the LEFT, it will deliver NULL in case a person has no associated phones.
To address your other question: It is not practical to split a commalist into the components.
postgresql_where is useful to get around the (in my opinion wrong, but apparently the SQL standard defines it) way in which Postgres defines unique-ness, where Null values are always unique. A typical example is shown below, where no item can have identical name+purpose+batch_id values (and None/Null is considered one unique value due to the second Index).
class Item(StoredObject, Base):
batch_id = Column(Integer, ForeignKey('batch.id'))
group_id = Column(Integer, ForeignKey('group.id'))
name = Column(Text, nullable=False)
purpose = Column(Text, nullable=False, default="")
__table_args__ = (
Index('idx_batch_has_value',
'group_id', 'name', 'purpose', 'batch_id',
unique=True,
postgresql_where=(batch_id.isnot(None))),
Index('idx_batch_has_no_value',
'group_id', 'name', 'purpose',
unique=True,
postgresql_where=(batch_id.is_(None))),
)
However, I want the same behaviour across two ids (batch_id and group_id), that is to say that no item can have identical name+purpose+batch_id+group_id values (None/Null is considered one unique value in both batch_id and group_id).
I can workaround this by creating a 'default' batch/group object with a fixed ID (say 0). This means I'd have to ensure that batch/group object exists, cannot be deleted, and that that id doesn't get re-appropriated for another 'real' batch/group objects (not to mention I'd have to remember to reduce all counts by one when using/writing functions which count how many batches/groups I have).
Do-able, and I'm about to do it now, but there must be a better way! Is there something like:-
postgresql_where = (batch_id.isnot(None) AND group_id.isnot(None))
That would solve the problem where, in my opinion, it is meant to be solved, in the DB rather than in my model and/or initialization code.
I am modeling my database in Cassandra, coming from RDBMS. I want to know how can I create a one-to-many relationship which is embedded in the same Column Name and model my table to fit the following query needs.
For example:
Boxes:{
23442:{
belongs_to_user: user1,
box_title: 'the box title',
items:{
1: {
name: 'itemname1',
size: 44
},
2: {
name: 'itemname2',
size: 24
}
}
},
{ ... }
}
I read that its preferable to use composite columns instead of super columns, so I need an example of the best way to implement this. My queries are like:
Get items for box by Id
get top 20 boxes with their items (for displaying a range of boxes with their items on the page)
update items size by item id (increment size by a number)
get all boxes by userid (all boxes that belongs to a specific user)
I am expecting lots of writes to change the size of each item in the box. I want to know the best way to implement it without the need to use super columns. Furthermore, I don't mind getting a solution that takes Cassandra 1.2 new features into account, because I will use that in production.
Thanks
This particular model is somewhat challenging, for a number of reasons.
For example, with the box ID as a row key, querying for a range of boxes will require a range query in Cassandra (as opposed to a column slice), which means the use of an ordered partitioner. An ordered partitioner is almost always a Bad Idea.
Another challenge comes from the need to increment the item size, as this calls for the use of a counter column family. Counter column families store counter values only.
Setting aside the need for a range of box IDs for a moment, you could model this using multiple tables in CQL3 as follows:
CREATE TABLE boxes (
id int PRIMARY KEY,
belongs_to_user text,
box_title text,
);
CREATE INDEX useridx on boxes (belongs_to_user);
CREATE TABLE box_items (
id int,
item int,
size counter,
PRIMARY KEY(id, item)
);
CREATE TABLE box_item_names (
id int PRIMARY KEY,
item int,
name text
);
BEGIN BATCH
INSERT INTO boxes (id, belongs_to_user, box_title) VALUES (23442, 'user1', 'the box title');
INSERT INTO box_items (id, item, name) VALUES (23442, 1, 'itemname1');
INSERT INTO box_items (id, item, name) VALUES (23442, 1, 'itemname2');
UPDATE box_items SET size = size + 44 WHERE id = 23442 AND item = 1;
UPDATE box_items SET size = size + 24 WHERE id = 23442 AND item = 2;
APPLY BATCH
-- Get items for box by ID
SELECT size FROM box_items WHERE id = 23442 AND item = 1;
-- Boxes by user ID
SELECT * FROM boxes WHERE belongs_to_user = 'user1';
It's important to note that the BATCH mutation above is both atomic, and isolated.
Technically speaking, you could also denormalize all of this into a single table. For example:
CREATE TABLE boxes (
id int,
belongs_to_user text,
box_title text,
item int,
name text,
size counter,
PRIMARY KEY(id, item, belongs_to_user, box_title, name)
);
UPDATE boxes set size = item_size + 44 WHERE id = 23442 AND belongs_to_user = 'user1'
AND box_title = 'the box title' AND name = 'itemname1' AND item = 1;
SELECT item, name, size FROM boxes WHERE id = 23442;
However, this provides no guarantees of correctness. For example, this model makes it possible for items of the same box to have different users, or titles. And, since this makes boxes a counter column family, it limits how you can evolve the schema in the future.
I think in PlayOrm's objects first, then show the column model below....
Box {
#NoSqlId
String id;
#NoSqlEmbedded
List<Item> items;
}
User {
#NoSqlId
TimeUUID uuid;
#OneToMany
List<Box> boxes;
}
The User then is a row like so
rowkey = uuid=<someuuid> boxes.fkToBox35 = null, boxes.fktoBox37=null, boxes.fkToBox38=null
Note, the form of the above is columname=value where some of the columnnames are composite and some are not.
The box is more interesting and say Item has fields name and idnumber, then box row would be
rowkey = id=myid, items.item23.name=playdo, items.item23.idnumber=5634, itesm.item56.name=pencil, items.item56.idnumber=7894
I am not sure what you meant though on get the top 20 boxes? top boxes meaning by the number of items in them?
Dean
You can use Query-Driven Methodology, for data modeling.You have the three broad access paths:
1) partition per query
2) partition+ per query (one or more partitions)
3) table or table+ per query
The most efficient option is the “partition per query”. This article can help you in this case, step-by-step. it's sample is exactly a one-to-many relation.
And according to this, you will have several tables with some similar columns. You can manage this, by Materialized View or batch-log(as alternative approach).