I think I'm kinda confused, I have a model whose some of the fields where to reference their detail/s (description/s) from another table.
eg.
**tblCustomers**
_______________________
Name | Address | Gender
-----------------------
A | A | M
B | B | M
C | C | F
**tblGender**
__________________
Code | Description
------------------
M | Male
F | Female
In my view here's how it look
Name A
Address A
Gender M <<< wherein what I wanted is something like
Name A
Address A
Gender M - Male
In my model, I am currently just doing something like these:
public function search($_id,$_curLevel)
{
// #todo Please modify the following code to remove attributes that should not be searched.
$criteria=new CDbCriteria;
$criteria->compare('Name',$this->name);
$criteria->compare('Address',$this->address);
$criteria->compare('Gender',$this->gender,true);
}
I know its pretty obvious, since the model is just looking into a single table, but I can't figure how to relate and use another table to be able to get the description from other reference tables.
The way I would do this, is to add a relation to my model.
I am assuming you have a model setup for the gender table, and it is called Gender. I would also rename your 'gender' column in your customer table to genderID or similar. I have renamed it for my example.
Add this code to your Customer Model:
public function relations()
{
return array(
'gender' => array(self::BELONGS_TO, 'Gender', 'genderID'),
);
}
Then once you have created this relationship, it is very easy to extract the information in your view.
You already have the model, so now the view looks like this:
echo $model->name;
echo $model->address;
echo $model->gender->description;
Note that 'gender' in the line above is referring to the 'gender' relation we created, not the column that you had named 'gender' in the description table (I renamed that to genderID).
If you just wanna the gender description then you can user the relation and display $model->gender->description; as descriped in the above answer
If you wanna a custom text as you mentioned "M - Male" then you have to add a public property and fill it in the afterFind() method
class Customer extends CActiveRecord {
public $_gender;
public function afterFind() {
$this->_gender = $this->gender . ' - ' . $this->gender->description;
return parent::afterFind();
}
}
note: that gender relation must be exist
Related
Hi Everyone just wondering if I am following best practices here.
I have step defintions like the following
public class StepDefinitions {
#DataTableType
public Author authorEntry(Map<String, String> entry) {
return new Author(
entry.get("firstName"),
entry.get("lastName"),
entry.get("famousBook"));
}
#Given("There are my favorite authors")
public void these_are_my_favourite_authors(List<Author> authors) {
// step implementation
}
}
and my feature file could then be something like
Feature: this is a feature
Scenario: this is a scenario
Given There are my favorite authors
|firstName| lastName |
| first | last |
Scenario: this is another scenario
Given There are my favorite authors
|firstName| lastName | famousBook |
| first | last | book |
So in the first step it will create an Author object but with famousBook == null.
Since I am creating objects used for REST requests and jackson will ignore null values is it okay to create objects like this?
You are using wrong data structure for your examples. You can refer to this resource giving the examples of different table types.
In your case you have a table
|firstName| lastName |
| first | last |
and try to parse it into Map<String, String> entry which would result in the following map:
[key1 = firstName, value1 = lastName]
[key2 = first, value2 = last]
If you need to treat it like header in top line then you need to parse it into List<Map<String, String>>
This is my first time using a polymorphic relationship.
I am creating a LMS where a Assignment can be allocated to a individual user or a team so reading the Laravel docs it seems that the Polymorphic Relationship will be a good way to go about it.
I have created 4 tables.
Users:
| id | username | password | created_at | updated_at |
Teams: | id | friendly_name | slug |
Team User: | id | user_id | team_id |
Assignment Allocation:
| id | assignment_id | assignmentable_type | assignmentable_id | created_at | updated_at
So when the assignment_allocations has data in it looks like this...
| id | assignment_id | assignmentable_type | assignmentable_id |
| 1 | 1 | App\Models\User | 1 |
However I get this error:
SQL: select * from users where users.assignmentable_id = 1 and users.assignmentable_id is not null and users.assignmentable_type = App\Models\User
SO obviously I have done something wrong, however I cannot for the life of me figure out what I've done wrong.
This is my functions that relate to this:
AssignmentAllocation.php
public function assignmentable()
{
return $this->morphTo();
}
User.php
public function assignments()
{
return $this->morphMany('App\Models\User', 'assignmentable');
}
Team.php
public function assignments()
{
return $this->morphMany('App\Model\Team', 'assignmentable');
}
Any help is greatly appreciated.
There are 2 potential answers depending on your data structure, which isn't clear from your question.
User/Team to Assignment is a one-to-many relationship (each Assignment has one User/Team, and each User/Team has many Assignments
Many-to-many relationship where each User/Team has many Assignments and each Assignment has many Users/Teams
One to many
In a one-to-many you wouldn't need an Assignment table and an AssignmentAllocation table, so I am assuming your AssignmentAllocation is your Assignment model. If not then you need to put the assignmentable_type and assignmentable_id columns on the assignments table instead, and use Assignment.php instead of AssignmentAllocation.php.
AssignmentAllocation.php
public function assignmentable()
{
return $this->morphTo();
}
User.php
public function assignments()
{
return $this->morphMany('App\Models\AssignmentAllocation', 'assignmentable');
}
Team.php
public function assignments()
{
return $this->morphMany('App\Models\AssignmentAllocation', 'assignmentable');
}
Your error is because Laravel is searching the users table for the match (you have morphMany('User'..), when it should be searching the AssignmentAllocation table. So just switch them out.
1) The morphMany acts like a hasMany, so you're saying:
Each User hasMany Assignments, and each Team hasMany Assignments. The first part of the morphMany says "search the AssignmentAllocation table", and the second part says "search for assignmentable_id and assignmentable_type being equal to this instance of this model".
2) morphTo acts like belongsTo so you're saying:
Each Assignment belongsTo an assignmentable.
Many to many
However if AssignmentAllocation is a many-to-many pivot table and each Assignment has many Users or Teams, and each User/Team has many Assignments, then you need a morphToMany/morphedByMany pair.
User.php
public function assignments()
{
return $this->morphToMany('App\Models\Assignment', 'assignmentable');
}
Team.php
public function assignments()
{
return $this->morphToMany('App\Models\Assignment', 'assignmentable');
}
Assignment.php model NOTE: not the AssignmentAllocation model
public function users()
{
return $this->morphedByMany('App\Models\User', 'assignmentable');
}
public function teams()
{
return $this->morphedByMany('App\Models\Team', 'assignmentable');
}
You should rename the assignment_allocation table to assignmentables, or add the required third and fourth arguments to the morph functions. I prefer to keep the table names consistent.
I have 3 tables that i basically need to display in the single gridview.
property
sale (FK property_id)
sale_staff (FK sale_id, staff_id)
staff
Now a staff member can be a lister, a seller, or both, and each sale can have multiple lister or sellers.
So table sale_staff has column 'staff_type' with possible enum values lister',seller`.
So the gridview must have a display something like
property street | sale price | sale date | lister | seller
and the lister/seller fields should be able to display simple initials comma separated like MJ, AB. This initial field comes from the staff table, which as above is linked to sale_staff via id.
I have not seen this type of scenario before so im simply unsure of how to incorporate the sale_type into the search so that only a staff listed as lister or seller is shown in that place.
Also, if i try something like this, just to see if anything is pulled back:
'attribute' => 'saleStaff.staff_id.initials'
or
'attribute' => 'saleStaff.staff'
I get (not set) in the gridview.
I have relations set up as:
Sale:
public function getProperty()
{
return $this->hasOne(Property::className(), ['id' => 'property_id']);
}
public function getSaleStaff()
{
return $this->hasMany(SaleStaff::className(), ['sale_id' => 'id']);
}
Sale_Staff:
public function getStaff()
{
return $this->hasOne(Staff::className(), ['id' => 'staff_id']);
}
public function getSale()
{
return $this->hasOne(Sale::className(), ['id' => 'sale_id']);
}
How do I match up the join table sale_staff with the sale table ? Is the structure ok or is DB incorrect ? I have done it this way to be flexible - any number of staff can be either a lister, seller, or both such as:
property street | sale price | sale date | lister | seller
Parkers Road | 400,000 | 22/06/2016| MJ | MJ, AB
Naturally, key question is how to represent multiple (unknown number of) values in a single gridview column like that.
Situation
I trying to set up a database schema to store translations, between different languages. So far it looks like this (simplyfied):
class Language(models.Model):
tag = models.CharField(max_length=2)
def __unicode__(self):
return self.tag
class Phrase(models.Model):
name = models.TextField()
language = models.ForeignKey(Language)
def __unicode__(self):
return self.name
class Meta:
unique_together = ("name", "language")
index_together = [
["name", "language"]
]
class Translation(models.Model):
phrase1 = models.ForeignKey(Phrase, related_name="translation_as_1")
phrase2 = models.ForeignKey(Phrase, related_name="translation_as_2")
def __unicode__(self):
return self.phrase1.name + " <=> " + self.phrase2.name
class Meta:
unique_together = ("phrase1", "phrase2")
index_together = [
["phrase1", "phrase2"]
]
This database schema seems logical to me. I store phrases in different languages and then have translations that contain exactly two phrases.
Problem
The problem is, that the queries, that result out of this schema, look kind of nasty. For instance:
from django.db.models import Q
name = "my phrase"
translations = Translation.objects.filter(Q(phrase1__name=text)|Q(phrase2__name=text))
translated_names = []
for translation in translations:
name1 = translation.phrase1.name
name2 = translation.phrase2.name
if name1 == name:
translated_names.append(name2)
else:
translated_names.append(name1)
I always have to include the "OR" relationship, to make sure, that I get all the possible translations, since the phrase could be stored as phrase1 or phrase2. On top of that, I have to filter my result afterwards to get the correct translated_name (for loop).
Further Explaination
Before I switched to the described schema, I had the following schema instead (Phrase and Language are the same as before):
class Translation(models.Model):
phrase = models.ForeignKey(Phrase)
name = models.TextField()
def __unicode__(self):
return self.phrase.name + " => " + self.name
class Meta:
unique_together = ("phrase", "name")
index_together = [
["phrase", "name"]
This schema let me make queries like this:
from django.db.models import Q
name = "my phrase"
translations = Translation.objects.filter(phrase__name=text)
translated_names = [t.name for t in translations]
This looks much nicer, and is of course faster. But this schema had the disadvantage, that it presents translations only in one direction, so I moved to the other one, which isn't quite what I want as well, because too slow and too complicated queries.
Question
So is there a good schema for this kind of problem, that I maybe overlook?
Remark
I'm not only interested in Django related answers. A pure SQL schema for this kind of problem would also be interesting for me.
This is the way that I have done it in the past. Adapt it for your naming convention.
Suppose that I had a table with a name and other columns in it like this
Table TR_CLT_clothing_type
clt_id | clt_name | other columns ....
--------------------------------------
1 | T Shirt ...
2 | Pants ...
Now if I decided that it needs translations, first I make a languages table
Table TR_LNG_language
lng_id | lng_name | lng_display
-------------------------------
1 | English | English (NZ)
2 | German | Deutsch
I also need to store the current language in the database (you will see why soon). It will only have one row
Table TA_INF_info
inf_current_lng
---------------
1
Then I drop the clt_name column from my clothing table TR_CLT_clothing_type. Instead I make relation table.
Table TL_CLT_clothing_type
clt_id | lng_id | clt_name
--------------------------
1 | 1 | T Shirt
1 | 2 | (German for T-Shirt)
2 | 1 | Pants
2 | 2 | keuchen (thank you google translate)
Now to get the name, you want to make a stored procedure for it. I have not attempted this in ORM.
CREATE PROCEDURE PS_CLT
#clt_id int
AS
SELECT lng.clt_name, clt.*
FROM TR_CLT_clothing_type clt
JOIN TL_CLT_clothing_type lng
ON lng.clt_id = clt.clt_id
WHERE clt.clt_id = #clt_id AND
lng.lng_id in (SELECT inf_current_lng FROM TA_INF_info)
This stored proc will return the name in the current language and all other columns for a specified language. To set the language, set the clt_current_lng in the TA_INF_info table.
Disclaimer: I don't have anything to check the syntax of what I have typed but it should hopefully be straightforward.
-- EDIT
There was a concern to be able to do "give me all translations for word X in language Y to language Z"
There is a "not so elegant" way to do this with the schema. You can do something like
for each table in database like "TL_%"
SELECT name
FROM table
WHERE id IN ( SELECT id
FROM table
WHERE name = #name
AND lng_id = german
)
AND lng_id = english
Now I would imagine that this would require some auto-generated SQL code but I could pull it off.
I have no idea how you would do this in ORM
I am trying to create a job application-form with Django.
Basically, I created two models.
softwareskill_model
application_model
The admin can log into the admin-section and add new softwareskill-
entries to the database. The application_model references those
softwareskill-entries/records using a ManyToMany-Field:
class softwareskill_model(django.db.models.Model):
name = django.db.models.CharField(max_length=200)
class application_model(django.db.models.Model):
# ...
softwareskills = django.db.models.ManyToManyField(softwareskill_model)
So if someone wants to apply for the job, he can select which
software-packages he uses.
Now I want the applicant to make a rating from 1-6 for each software-skill
he has selected. How do you do that?
I am using a SQLite3 database and discovered that the ManyToManyField
creates a new table to store the relationship. In my case it looks like
this:
| ID | application_model_id | softwareskill_model_id |
My assumption would be to simply add a new column so it looks like this:
| ID | application_model_id | softwareskill_model_id | Rating |
Is that possible / the best way to do it? How?
I am very new to Django, databases and web-development in general and hope
you can help me :-)!
Thank you,
Henry
through is what you need to use, e.g.
class softwareskill_model(django.db.models.Model):
name = django.db.models.CharField(max_length=200)
class application_model(django.db.models.Model):
# ...
softwareskills = django.db.models.ManyToManyField(softwareskill_model, through="ApplicationSoftwareSkill")
class ApplicationSoftwareSkill(models.Model):
softwareskill = models.ForeignKey(softwareskill_model)
application = models.ForeignKey(application_model)
# extra fields here e.g.
rating = models.IntegerField()