How to add custom field in rails3? - ruby-on-rails-3

I want to give an option in my application to let the use choose maximum 3 custom fields of his choice, how is it possible ? I know their is some easy solution for this but can't get that.
I mean is it possible in rails to let the user select the label for extra fields he wants in his data. FOr example i have a table fruit with fields name, type, taste and two extra fields extra_field1 and extra_field2. The first 3 fields are visible on the form with same label and a link to add custom fields, after clicking on that link user gets a form where he can label the extra fields of his choice like person A name them rate and weight while person B name them rate and color etc. After the name setting the extra field should be visible to user on the form every time he creates a new record.

One solution can be that you can have model CustomField, set available custom fields in custom_fields table. Use multi select dropdown to allow user to select custom fields, as many as he want.
And there will be an intermediate model which will have user_id, 'custom_fields_id' and value.
Your associations might be like this.
user.rb
has_many :custom_fields, :through => 'UserCustomFields'
user_custom_field.rb
belongs_to :user
belongs_to :custom_field
custom_field.rb
has_many :users, :through => 'UserCustomFields'

I have added a new table to database with name "custome_field" that contains the followitn columns:
mysql> desc custom_fields;
+---------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| company_id | int(11) | NO | | NULL | |
| voucher_type | varchar(255) | NO | | NULL | |
| custom_label1 | varchar(255) | YES | | NULL | |
| custom_label2 | varchar(255) | YES | | NULL | |
| custom_label3 | varchar(255) | YES | | NULL | |
| created_at | datetime | YES | | NULL | |
| updated_at | datetime | YES | | NULL | |
+---------------+--------------+------+-----+---------+----------------+
And added three new column to my table a as custom_field1, custom_field2 and custom_field3.
Now i can set label in custom_field table and class for which i want the custom fields and use them in form i need.
Thanks to #Ramiz Raja for supporting me on this.

Related

How should I design a table where a row can have different columns depending on the type of row?

I'm planning to use the Reddit API and store my saved posts in a database. The saves can be of two types - Comments or Posts, both of them have few common columns - author, score, subreddit etc. and a few columns unique to each category:
comment - body_text, comment_id, parent_id, etc.
posts - selftext,link_url,is_video, etc.
I decided to separate the 2 categories into their own tables - Comments table and Posts table. But I don't know how to link these tables to the master table "saves".
My current solution is to have a column kind for the type of save. The comment_id and post_id link the save to its own table. However, this feels like a messy solution and a bit cumbersome. A save can either have a comment_id or a link_id (but not both or neither), and I also have to manage this constraint.
Saves Table :
+----+---------+-------+---------------------------------------------+---------+------------+---------+
| ID | Kind | title | post_url | author | comment_id | post_id |
+----+---------+-------+---------------------------------------------+---------+------------+---------+
| 1 | comment | abc | https://redd.i/redditpostid/redditcommentid | FusionX | 1 | NULL |
| 2 | post | xyz | https://redd.i/redditpostid | XnoisuF | NULL | 1 |
+----+---------+-------+---------------------------------------------+---------+------------+---------+
Post Table :
+----+---------+-------------------------------------------+-----------------------+--------------+--------------+
| ID | is_self | selftext | post_url | num_comments | thumbnail |
+----+---------+-------------------------------------------+-----------------------+--------------+--------------+
| 1 | no | NULL | i.imgur.com/xyz.jpg | 1020 | someimageurl |
| 2 | yes | "some random selftext of variable length" | redd.it/redditpostid/ | 10 | |
+----+---------+-------------------------------------------+-----------------------+--------------+--------------+
Comment table:
+----+---------------------------------+---------------------+--------------------+
| ID | body_html | reddit_comment_id | reddit_parent_id |
+----+---------------------------------+---------------------+--------------------+
| 1 | comment text of variable length | <reddit comment id> | <reddit parent id> |
+----+---------------------------------+---------------------+--------------------+
(reddit ID's are different from my table's own IDs and are only relevant at reddit's end)
Is there a better way to design this database?
I think you should move the owning side of the relation to the two other tables.
So instead of having comment_id and post_id columns in saves table, have a saves_id column in post table and comment table.

Building a Database Model for Role Based Access Control

I'm trying to make a role based access control system, but the problem comes when I approach the Database Part of It.
Should I make two models, Role and Permission, and then make a many to many relationship between role and permission or what?
My User Model looks something like this:
Column | Type | Collation | Nullable | Default
------------+-----------------------------+-----------+----------+--------------------
id | uuid | | not null | uuid_generate_v4()
name | character varying(50) | | not null |
email | character varying(320) | | not null |
avatar | text | | |
password | text | | not null |
phone | character varying(30) | | |
created_at | timestamp without time zone | | not null | now()
updated_at | timestamp without time zone | | | now()
companyId | uuid | | |
roleId | uuid | | |
So I just have 1 to many relation ship between user and role.
Mainly it's done with a single role and multiple users associated with it.
Roles can be assigned privileges and users can be assigned roles.
You can refer following for the same.
Role-based access control in Postgres/mongo

SQL relationships and best practices

If I have a User table and a Roles table.
What is the usual practice/pattern for adding the relationship?
Do I create an extra column in the User table for the RoleID or do people usually create a Relationships table like so:
Relationships Table
RelationshipID | UserID | RoleID |... any other relations a user might have
for the last bit, as a user you might create an endless amount of different types of things that all need to be related to you... do you instead add the relationship to each individual table created for each individual thing.. for example:
Pages Table
PageID | Title | Content | Author (UserID)
and so another table would also be similar to this:
Comments Table
CommentID | Comment | Author (UserID)
In this case, I would need to expand upon the Relationships table if I were to do it that way:
Relationships Table
RelationshipID | UserID | RoleID | CommentID
and i'd probably only want to fill in the UserID and CommentID as this relationship is not for the Roles... that is governed by another entry. so for example the values might be put in for a comment relationship:
AUTO | 2 | NULL | 16
I could imagine a multi purpose Revisions table being handy...
Revisions Table
RevisionID | DateCreated | UserID | ActionTypeID | ModelTypeID | Status | RelatedItemID
---------------------------------------------------------------------------------------
1 | <Now> | 3 | 4 (Delete) | 6 (Page) | TRUE | 38
2 | <Now> | 3 | 1 (Delete) | 5 (Comment) | TRUE | 10
3 | <Now> | 3 | 1 (Add) | 5 (Comment) | FALSE | 10
but not for a general Relationships table...
Does this sound correct?
Edit since comments:
They stated that the relationships table should be made due to the many-to-many (data model)
So let's take my previous example of my possible relationship table:
Relationships Table Old
RelationshipID | UserID | RoleID | CommentID... etc
Should it actually be something more like this:
Relationships Table New
RelationshipID | ItemID | LinkID | ItemType | LinkType | Status
---------------------------------------------------------------------------------
1 | 23(PageID) | 7(UserID) | ("Page") | ("User") | TRUE
2 | 22(CommentID) | 7(UserID) | ("Comment") | ("User") | TRUE
3 | 22(CommentID) | 23(PageID) | ("Comment") | ("Page") | TRUE

SQL design for notification of new registered users

I'm with a great difficulty in formulate a SQL for a module of notifications when a new user register.
I have a database of Notifications, I set up a notification to be sent. Examples:
Send notification when a man and blue eyes register;
Send notification when a woman register;
Send a notification when a blue-eyed woman, brown and work in the company Foo;
With these rules we can see that there can be several possibilities (so the table columns are optional).
Some details:
The table columns are defined as integers because are FK. I just did not put tables because the structure is unnecessary, since the SQL will only relashionship between User and Notification;
The date field is used to store both the date of registration of the notice of such person. So I can only filter to notify the new register of user;
Table Structure
User:
+------------+----------+------+-----+---------+------------+
| Field | Type | Null | Key | Default | Extra |
+------------+----------+------+-----+---------+------------+
| Id | int(11) | NO | PRI | | auto_incre |
| Gender | int(11) | YES | | | |
| HairColor | int(11) | YES | | | |
| EyeColor | int(11) | YES | | | |
| Company | int(11) | YES | | | |
| Date | datetime | NO | | | |
| ... | | | | | |
+------------+----------+------+-----+---------+------------+
Notification:
+------------+----------+------+-----+---------+------------+
| Field | Type | Null | Key | Default | Extra |
+------------+----------+------+-----+---------+------------+
| Id | int(11) | NO | PRI | | auto_incre |
| Gender | int(11) | YES | | | |
| HairColor | int(11) | YES | | | |
| EyeColor | int(11) | YES | | | |
| Company | int(11) | YES | | | |
| Date | datetime | NO | | | |
+------------+----------+------+-----+---------+------------+
Initial idea
The initial idea I had was doing a select for each possibility and joining via union:
-- Selects new users by gender notification
SELECT *
FROM Notification
inner join User on (
User.Date >= Notification.Date and
Notification.Gender = User.Gender and
Notification.HairColor is null and
Notification.EyeColor is null and
Notification.Company is null
)
union all
-- Selects new users by gender and hair color notification
SELECT *
FROM Notification
inner join User on (
User.Date >= Notification.Date and
Notification.Gender = User.Gender and
Notification.HairColor = User.HairColor and
Notification.EyeColor is null and
Notification.Company is null
)
-- ... and so on, doing a select for each option, resulting in 16 selects (4 columns: gender, hair color, eye color and company)
My question is:
Is there another way I can do this SQL querying all the possibilities of notifications in a more easy?
Following this structure of 4 columns we already have 16 selects. In my real structure will have more columns with something unfeasible to keep it that way.
Is there any other suggestion storage structure of the data for a better way to do this functionality?
SELECT *
FROM Notification
inner join User on (
User.Date >= Notification.Date and
(Notification.Gender is null or Notification.Gender = User.Gender) and
(Notification.HairColor is null or Notification.HairColor = User.HairColor) and
(Notification.EyeColor is null Notification.EyeColor = User.EyeColor) and
(Notification.Company is null or Notification.Company = User.Company)
)
This way you get every set of user with the notification stored in the tables.
This is the way I would implement this user registration / notification functionality:
Three tables: Users, Notif_type, Notif_queue.
A trigger on insert on table Users which calls a stored procedure SendNotification(user_id).
The stored proc will have the logic which you can change overtime without having to modify the schema/data. The logic will be:
to select the type of notification (form Notif_type) the new user should receive based on your rules;
to insert a row in Notif_queue which holds a FK to user_id and notif_type_id, so that the functionality notifying the user is completely de-coupled from the notification rules.
why can't you just use the one table "user" and put an extra field/flag called [Notified] so that every time you want to send notifications just refer it to the flag.
i find it irrelevant to use the notification table.

Are there problems with this 'Soft Delete' solution using EAV tables?

I've read some information about the ugly side of just setting a deleted_at field in your tables to signify a row has been deleted.
Namely
http://richarddingwall.name/2009/11/20/the-trouble-with-soft-delete/
Are there any potential problems with taking a row from a table you want to delete and pivoting it into some EAV tables?
For instance.
Lets Say I have two tables deleted and deleted_row respectively described as follows.
mysql> describe deleted;
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| tablename | varchar(255) | YES | | NULL | |
| deleted_at | timestamp | YES | | NULL | |
+------------+--------------+------+-----+---------+----------------+
mysql> describe deleted_rows;
+--------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| entity | int(11) | YES | MUL | NULL | |
| name | varchar(255) | YES | | NULL | |
| value | blob | YES | | NULL | |
+--------+--------------+------+-----+---------+----------------+
Now when you wanted to delete a row from any table you would delete it from the table then insert it into these tables as such.
deleted
+----+-----------+---------------------+
| id | tablename | deleted_at |
+----+-----------+---------------------+
| 1 | products | 2011-03-23 00:00:00 |
+----+-----------+---------------------+
deleted_row
+----+--------+-------------+-------------------------------+
| id | entity | name | value |
+----+--------+-------------+-------------------------------+
| 1 | 1 | Title | A Great Product |
| 2 | 1 | Price | 55.00 |
| 3 | 1 | Description | You guessed it... it's great. |
+----+--------+-------------+-------------------------------+
A few things I see off the bat.
You'll need to use application logic
to do the pivot (Ruby, PHP, Python,
etc)
The table could grow pretty big
because I'm using blob to handle
the unknown size of the row value
Do you see any other glaring problems with this type of soft delete?
Why not mirror your tables with archive tables?
create table mytable(
col_1 int
,col_2 varchar(100)
,col_3 date
,primary key(col_1)
)
create table mytable_deleted(
delete_id int not null auto_increment
,delete_dtm datetime not null
-- All of the original columns
,col_1 int
,col_2 varchar(100)
,col_3 date
,index(col_1)
,primary key(delete_id)
)
And then simply add on-delete-triggers on your tables that inserts the current row in the mirrored table before the deletion? That would provide you with dead-simple and very performant solution.
You could actually generate the tables and trigger code using the data dictionary.
Note that I might not want to have a unique index on the original primary key (col_1) in the archive table, because you may actually end up deleting the same row twice over time if you are using natural keys. Unless you plan to hook up the archive tables in your application (for undo purposes) you can drop the index entirely. Also, I added the time of delete (deleted_dtm) and a surrogate key that can be used to delete the deleted (hehe) rows.
You may also consider range partitioning the archive table on deleted_dtm. This makes it pretty much effortless to purge data from the tables.