I feel pretty darn dumb for asking this question but I'm baffled, probably because I'm slightly intimidated with cakePHP(new to it, and not very comfortable),but its merits are practically forcing me to use it. So please if you could help, I appreciate it :
My problem is with understanding how Model association works. After going through all the section about associations here I am still struggling and actually am not quite sure whether either my database design is flawed, or the associations between the models, are wrong.
Here it is : I have 3 Models, which are :
Donor, BloodGroup, DonorType
To start with, I am not completely sure if I should create a model for every table in the db.
My tables look like this :
+-------donors-------+
| donor_id (pk) |
| name |
| surname |
| donor_type_id (fk) |
| blood_group_id (fk)|
| |
+--------------------+
+----donor_types-----+
| type_id (pk) |
| type |
| |
+--------------------+
+----blood_groups----+
| blood_group_id(pk) |
| group |
| |
+--------------------+
Donor Model
class Donor extends AppModel{
public $hasOne = array(
'BloodGroup'=> array(
'className' => 'BloodGroup'
),
'DonorType' => array(
'className' => 'DonorType'
)
);
According to the tables in the database, above is how the relationships can be described. I am already using the association to register a new donor with values retrieved from the blood group table/model, using this code in my controller:
$this->set('bloodGroups', $this->Donor->BloodGroup->find('list',
array('fields' => array(
'BloodGroup.blood_group_id','BloodGroup.blood_group'
)
)
));
Therefore I think the association is correct. However, now I want to display the stored users with the relevant details, but an sql error is being generated :
Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'BloodGroup.donor_id' in 'on clause'
This is ofcourse because there is no 'donor_id' as a fk inside the BloodGroup table, by actually there is a blood_group_id as a fk inside the donor table.
So what is the solution to this? Is the db design flawed or is there a way I can redefine the association within cake ?
There appear to be 2 things wrong with your setup.
You are not following Cake database conventions. Unless your hands are tied (e.g. external datasource you have no control over), your primary key should always be called id, not table_name_id. Cake always looks for id keys by default. If you have no choice, you can set a custom primary key by setting the primaryKey property in your model.
By looking at your tables, you seem to be mixing up hasOne with belongsTo. Even though in plain English it sounds logical that a donor has one blood group, from a database standpoint, the donor belongs to a certain blood group. The manual has some notes that help you pick the right one:
hasOne: the other model contains the foreign key.
belongsTo: the current model contains the foreign key.
This is also illustrated with some examples in the documentation (see links above). The latter one is true in your case, so your model should eventually look like this:
class Donor extends AppModel {
public $belongsTo = array(
'BloodGroup',
'DonorType'
);
}
Related
I am creating a site that allows users to add a number of various items to their pages. These items can be very different from each other, such as text, images, numbers, etc. To handle this, I have one table that stores one entry for each item. The table has an id, the location of the item on the page, and the type of the item. I then have one table for each type of item which holds the more specific information about that item.
What I want to do is to set up an association so that when the page loads, it grabs the items from the main table in order - which is simple - and then also grabs the associated information from the proper table of specific information.
An example main_table could be:
id | location | type
1 | 1 | text
2 | 2 | image
3 | 3 | plot
4 | 4 | question
The table for text items may look like:
id | text |
1 | aaa |
2 | bbb |
The table for plots may look like:
id | type | datasets |
1 | bar | 3
2 | scatter | 2
So, can I set up an association where the main_data model pulls in the one associated file from the proper table? I could set up a separate model that selected the table for the specific information based on a passed in variable and set the source, but I am not sure how you would pass the variable with the type information to the model, nor am I sure if I am approaching this properly?
Thanks
PS
I had thought of the obvious - to combine the items into one table and simply have empty fields for everything not related to that item for each entry. So a text entry would still have columns for plot stuff, but they wouldn't be filled, etc. However, I wanted to be less wasteful of space - unless everyone thinks it would work better.
Sorry I don't have the reputation to comment for clarification, but this is what I made of your question. I think you're overthinking it...
The one thing that sticks out about your question, is that things are going to get very messy with the way you're approaching it.
In my opinion the best way to handle this is to give each item type it's own table then associations in the model definitions.
This is how your tables will look like, remember CakePHP tables need to be plural and user_id is deliberately titled as such.
Users
id | username
1 | example
Questions
id | user_id | text
1 | 1 | this is a question
Plots
id | user_id | text
1 | 1 | this is plot
This in your app/Model folder you will need files called User.php & Question.php & Plot.php (singular naming convention) that should look something like this
User.php
<?php
public $hasMany = array(
'Question',
'Plot'
);
?>
Question.php
<?php
public $belongsTo = 'User';
?>
Plot.php
<?php
public $belongsTo = 'User';
?>
Once you have set up the above, you will be able to easily access Plots and Questions in your views, I've provided below a snippet of a potential users controller and expected usage you can expect to use... learn to use debug() in CakePHP it's fantastic.
Controller/UsersController.php
<?php
...
public function index() {
$this->set('users', $this->paginate());
}
...
?>
View/Users/index.ctp
<div>
<?php
foreach ($users as $user):
debug($user);
echo $user['Question']['text'];
echo $user['Plot']['text'];
endforeach;
unset($user);
?>
</div>
After reading your question again, you mention "order" and "location". You can handle this by adding a column for "date_created" and "page_position" to each different table. I still feel like you overcomplicated your solution, and I hope I didn't miss something.
I want to understand how to use ReferenceProperty for common usage scenarios.
In a typical application, we always display Referenced Entity's columns.
For example, consider a Purchase Order application.
class POCategory(db.Model):
name = db.StringProperty()
class POSubCategory(db.Model):
category = db.ReferenceProperty(POCategory, collection_name='sub_categories')
name = db.StringProperty()
class PurchaseOrder(db.Model):
total_amount = db.FloatProperty()
class PurchaseOrderLineItem(db.Model):
category = db.ReferenceProperty(POCategory, collection_name='po_line_items')
sub_category = db.ReferenceProperty(POSubCategory, collection_name = 'po_line_items')
amount = db.FloatProperty()
This is what we normally display in typical applications.
+---------------+---------------+--------+
| Category | Sub Category | Amount |
+---------------+---------------+--------+
| Blue Category | Water | $12.00 |
| Red Category | Fire | $20.00 |
+---------------+---------------+--------+
| Purchase Order Total | $22.00 |
+---------------+---------------+--------+
Should I be using ReferenceProperty Pre-fetching for this to avoid N+1 select problem?
or
duplicate the category and sub-category names in my Purchase Order Line Item like below?
class PurchaseOrderLineItem(db.Model):
category = db.ReferenceProperty(POCategory, collection_name='po_line_items')
category_name = db.StringProperty()
sub_category = db.ReferenceProperty(POSubCategory, collection_name = 'po_line_items')
sub_category_name = db.StringProperty()
amount = db.FloatProperty()
Obviously, the names for categories and sub-categories are editable.
So when someone updates the name property, I will have to query and loop over all referenced PurchaseOrderLineItem entities and update my duplicate name properties.
#----------------------------------------
# BAD DESIGN
#----------------------------------------
po_category.name = 'New Category Name'
# build list of line items to be updated
update_list = []
for child_line_item in po_category.po_line_items:
child_line_item.category_name = po_entity.name
update_list.append(child_line_item)
db.put(po_category, update_list)
I know it is NOT a good scalable solution because over time we will have a lot of Line Items to be updated. RDBMS way of thinking is hard to get rid of.
So can anyone please teach me how to think for these kinds of typical scenarios?
Thanks!
As you said, since you can often edit category name, you should not embed it into Line Item.
Instead use NDB (which automatically caches gets) and multi-get (one DB call to get multiple entities) to get categories and sub-categories.
I'm putting together a database that I need to normalize and I've run into an issue that I don't really know how to handle.
I've put together a simplified example of my problem to illustrate it:
Item ID___Mass___Procurement__Currency__________Amount
0__________2kg___inherited____null________________null
1_________13kg___bought_______US dollars_________47.20
2__________5kg___bought_______British Pounds______3.10
3_________11kg___inherited____null________________null
4__________9kg___bought_______US dollars__________1.32
(My apologies for the awkward table; new users aren't allowed to paste images)
In the table above I have a property (Amount) which is functionally dependent on the Item ID (I think), but which does not exist for every Item ID (since inherited items have no monetary cost). I'm relatively new to databases, but I can't find a similar issue to this addressed in any beginner tutorials or literature. Any help would be appreciated.
I would just create two new tables ItemProcurement and Currencies.
If I'm not wrong, as per the data presented, the amount is part of the procurement of the item itself (when the item has not been inherited), for that reason I would group the Amount and CurrencyID fields in the new entity ItemProcurement.
As you can see, an inherited item wouldn't have an entry in the ItemProcurement table.
Concerning the main Item table, if you expect just two different values for the kind of procurement, then I would use a char(1) column (varying from B => bougth, I => inherited).
I would looks like this:
The data would then look like this:
TABLE Items
+-------+-------+--------------------+
| ID | Mass | ProcurementMethod |
|-------+-------+--------------------+
| 0 | 2 | I |
+-------+-------+--------------------+
| 1 | 13 | B |
+-------+-------+--------------------+
| 2 | 5 | B |
+-------+-------+--------------------+
TABLE ItemProcurement
+--------+-------------+------------+
| ItemID | CurrencyID | Amount |
|--------+-------------+------------+
| 1 | 840 | 47.20 |
+--------+-------------+------------+
| 2 | 826 | 3.10 |
+--------+-------------+------------+
TABLE Currencies
+------------+---------+-----------------+
| CurrencyID | ISOCode | Description |
|------------+---------+-----------------+
| 840 | USD | US dollars |
+------------+---------+-----------------+
| 826 | GBP | British Pounds |
+------------+---------+-----------------+
Not only Amount, everything is dependent on ItemID, as this seems to be a candidate key.
The dependence you have is that Currency and Amount are NULL (I guess this means Unknown/Invalid) when the Procurement is 'inherited' (or 0 cost as pointed by #XIVsolutions and as you mention "inherited items have no monetary cost")
In other words, iems are divided into two types (of procurements) and items of one of the two types do not have all attributes.
This can be solved with a supertype/subtype split. You have a supertype table (Item) and two subtype tables (ItemBought and ItemInherited), where each one of them has a 1::0..1 relationship with the supertype table. The attributes common to all items will be in the supertype table and every other attribute in the respecting subtype table:
Item
----------------------------
ItemID Mass Procurement
0 2kg inherited
1 13kg bought
2 5kg bought
3 11kg inherited
4 9kg bought
ItemBought
---------------------------------
ItemID Currency Amount
1 US dollars 47.20
2 British Pounds 3.10
4 US dollars 1.32
ItemInherited
-------------
ItemID
0
3
If there is no attribute that only inherited items have, you even skip the ItemInherited table altogether.
For other questions relating to this pattern, look up the tag: Class-Table-Inheritance. While you're at it, look up Shared-Primary-Key as well. For a more concpetual treatment, google on "ER Specialization".
Here is my off-the-cuff suggestion:
UPDATE: Mass would be a Float/Decimal/Double depending upon your Db, Cost would be whatever the optimal type is for handling money (in SQL Server 2008, it is "Money" but these things vary).
ANOTHER UPDATE: The cost of an inherited item should be zero, not null (and in fact, there sometime IS an indirect cost, in the form of taxes, but I digress . . .). Therefore, your Item Table should require a value for cost, even if that cost is zero. It should not be null.
Let me know if you have questions . . .
Why do you need to normalise it?
I can see some data integrity challenges, but no obvious structural problems.
The implicit dependency between "procurement" and the presence or not of the value/currency is tricky, but has nothing to do with the keys and so is not a big deal, practically.
If we are to be purists (e.g. this is for homework purposes), then we are dealing with two types of item, inherited items and bought items. Since they are not the same type of thing, they should be modelled as two separate entities i.e. InheritedItem and BoughtItem, with only the columns they need.
In order to get a combined view of all items (e.g. to get a total weight), you would use a view, or a UNION sql query.
If we are looking to object model in the database, then we can factor out the common supertype (Item), and model the subtypes (InheritedItem, BoughtItem) with foreign-keys to the supertype table (ypercube explanation below is very good), but this is very complicated and less future-proof than only modelling the subtypes.
This last point is the subject of much argument, but practically, in my experience, modelling concrete supertypes in the database leads to more pain later than leaving them abstract. Okay, that's probably waaay beyond what you wanted :).
A noob question. I'm putting together my first database and have the following design problem: I have a class which defines a book (e.g. it's title) and a class which defines a page (e.g. it's design).
The table for the class book would look so:
Title | PAGE1 | PAGE2 | PAGE3
Book-One | SAMPLE1-UUID | SAMPLE2-UUID | SAMPLE3-UUID
Book-Two | SAMPLE4-UUID | SAMPLE5-UUID | SAMPLE6-UUID
The table for the class page:
UUID | FONT | CONTENTS etc.
SAMPLE1-UUID | Times | Example
SAMPLE2-UUID | Arial | Example Two
SAMPLE3-UUID | Verdena | Example Three
Now, as each page is unique and can't be re-used in another book, I can't use a many-to-many relationship for Pages. I could use Foreign-Key to link the two tables, i.e. link SAMPLE1-UUID of the Books Table with the SAMPLE1-UUID of the Pages Table. This has the advantage of not creating the same entry twice.
However, I don't like the idea of having a fixed amount of rows for my pages. In the above example for the class Book, I'd have to define a certain set of Pages, like PAGE1, PAGE2, PAGE3, PAGE4, ... PAGE99. Ideally, all I need is a flexible list of pages for my book class, like so:
Name | Pages
Book-One | "SAMPLE1-UUID, SAMPLE2-UUID"
Book-Two | "SAMPLE4-UUID, SAMPLE5-UUID, SAMPLE6-UUID"
Pages would be a simple CharField and its contents would be a list. But then I have the problem that the two tables are not linked anymore and that I'd have to create each entry twice (i.e. I would have to enter SAMPLE1-UUID in both the pages and books table).
Is there another way to design this database? Thanks for any suggestion!
I'll suggest you don't have the pages as columns:
The table for the class book would look so with book only information:
Title | ISBN
Book-One | XXXXXXXXXXXX
Book-Two | YYYYYYYYYYYY
The table for the class page:
BOOKID |PAGE_NUM | FONT | CONTENTS
1 |1 | Times | Example
1 |2 | Arial | Example Two
2 |1 | Verdena | Example Three
Your class design would look something like:
class Book(models.Model):
title = models.CharField(max_length=100)
isbn = models.CharField(max_length=100)
class Page(models.Model):
book = models.ForeignKey(Book)
page_num = models.IntegerField()
font = models.charField(max_length=100)
content = models.TextField()
You can go ahead and have contraints so that a book and page_num does not repeat for instance but this can be a good start.
I would do it like this:
class Book(models.Model):
name=models.CharField(max_length=....)
class Page(models.Model):
book=models.ForeignKey(Book)
number=models.PositiveIntegerField()
I don't understand your book table example: Do you want a column for page1 and an other column for page2? That looks very strange.
You've misunderstood how a foreign key works. It's not a "fixed amount of rows" - just the opposite, in fact.
As guettli shows in his answer, a ForeignKey field is a one-to-many relationship, defined on the "many" side. That is, with the ForeignKey defined on Page pointing at Book, each Page has one Book, but a Book has as many Pages as you like.
So, using the Django ORM, if you have a book object and you want to get all its pages, you just do my_book.page_set.all().
I have been struggling and searching for a solution about this for a couple of days but i cannot find any "best practices" or good explanations of how to achive what i want.
Lets say that I have a database consisting of the following tables (just an example);
Customers (Fields: Id, CustomerName, Location)
Products (Fields: Id, ProductName, ProductCode)
Groups (Fields: Id, GroupName)
I then need to link these together to be shown in a Treeview. For example;
Customer1
|
|-Group1
| |-Product1
| |-Product2
|
|-Group2
|-Product2
|-Product3
|-Group3
|
|-Product1
|-Product4
As i said, this is just an example. The real solution consists of other types.
Since the products can occur in several places i need to create a "link table"
to display the hierarchial data.
So i created another table looking like this;
Id (int)
ParentId (int)
ObjectType (int)
GroupId (int)
ProductId (int)
CustomerId (int)
The reason for the ObjectType field is to know in what database i need to check for the items name etc. to display in the treeview.
My question now: Is there any other way to design this database?
I am developing in C# using LINQ etc.
from your example, each level of the tree should be a new link table.
you do not show if group 1 is repeated for more than one customer, should the contents of group1 also be repeated.? but i assume group1 contents are the same no matter which customers are associated.
if you can truly link anything to anything, then the objectType is the way to go... but you would have something like:
parentId
ParentObjectType
childId
childObjectType