When to seperate database column meaning (generic columns) - database

I'm designing a feedback system for a site which has approximately 20 questions, but those questions may change each year and trying to consider the best design.
The simplest way is to map each question into a column, but this would require creating a new table each year and changing the application code which isn't sensible at all.
The second option is to separate the columns from the meaning and have a second table that applies meaning, i.e.
Table1: (row per survey) Table2: (row per questionnaire type)
QuestionID AnswerID
Question1 QuestionID
Question2 Answer1
... ....
Question20 Answer20
The third option I can think of is to completely separate each aspect like:
Table1: Table2: Table3: Table4:
QuestionID AnswerID MatchTableID SetID
QuestionValue AnswerValue QuestionID FeedbackSet
AnswerID QuestionID
Which gives it the benefit of scalability, but it may be excessive for something changing at most once a year for a few hundred records and i'm not sure if that doesn't feel a bit too much like an Entity-Attribute-Value design.
I'd appreciate any comments on what is considered to be best practice here and what is considered acceptable practice.

You might want to just keep it simple and go with a classic relational setup.
The models:
class Question < ActiveRecord::Base
has_many :replies
has_many :answers
belongs_to :questionnaire
end
class Answer < ActiveRecord::Base
belongs_to :question
has_many :replies
end
# A collection of questions
class Questionnaire < ActiveRecord::Base
has_many :questions
has_many :replies, through: :questions
end
# A user reply to a question
# acts as a many to many join table
class Reply < ActiveRecord::Base
belongs_to :question
belongs_to :answer
end
The schema:
ActiveRecord::Schema.define(version: 20160215124045) do
create_table "answers", force: :cascade do |t|
t.text "text"
t.integer "question_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "answers", ["question_id"], name: "index_answers_on_question_id"
create_table "questionnaires", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "questions", force: :cascade do |t|
t.text "text"
t.integer "questionnaire_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "questions", ["questionnaire_id"], name: "index_questions_on_questionnaire_id"
create_table "replies", force: :cascade do |t|
t.integer "question_id"
t.integer "answer_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "replies", ["answer_id"], name: "index_replies_on_answer_id"
add_index "replies", ["question_id"], name: "index_replies_on_question_id"
end
Yeah its a lot of tables - but this gives you a great deal of flexibility without dealing with the hassle that is key value tables. And its also much faster to build than some hacky attempt at a dynamic schema.
Its also really easy to pull out metrics if needed.
This example makes quite a few assumptions:
The questions are multiple choice - not write in. You can quite simply adapt it by storing the write-ins on the replies table.
The relation between question and questionnaire is one-to-one. Use a join table and HABTM relationship or has_many through: if need to be able to reuse questions on multiple questionnaires.

Related

django specify column arrangement in SQL Server

I am using Django ORM to create my database tables and perform the rest of the operations. However, I've noticed that the columns if I see in the database are not in the order I specify in the django models.
For example, my model is
class ItemMetaData(models.Model):
item = models.ForeignKey(Item, on_delete=CASCADE)
field = models.CharField(max_length=80)
field_value = models.CharField(max_length=80)
created_at = models.DateTimeField(auto_now_add=True)
modified_at = models.DateTimeField(auto_now=True)
But in the database when I see the table, the foreign key is the last column so it looks like
id | field | field_value | created_at | modified_at | item_id
I would like my item_id to be the first column after id . Like this:
id | item_id | field | field_value | created_at | modified_at
Is this possible? I tried to search but when I look for ordering all the answers assume it's row ordering and suggest using order_by
Migration file looks like this:
migrations.CreateModel(
name='ItemMetaData',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('field', models.CharField(max_length=80)),
('field_value', models.CharField(max_length=80)),
('created_at', models.DateTimeField(auto_now_add=True)),
('modified_at', models.DateTimeField(auto_now=True)),
],
options={
'db_table': 'app_item_meta_data',
},
),
migrations.AddField(
model_name='itemmetadata',
name='item',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app.item'),
),
The reason that this happens is because Django postpones creating ForeignKeys, ManyToManyFields, OneToOneFields, etc. after the models are constructed. Indeed, this is sometimes necessary, for example if you would construct models M1 and M2, and they both have a ForeignKey to refer to each other. In that case one can not create the first model and create the ForeignKey in the same CreateModel migration, since at that time M2 does not yet exists.
If you want to change the order of fields, you can work with a migration where you add the items after the ForeignKey is added, for example with:
migrations.CreateModel(
name='ItemMetaData',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
],
options={
'db_table': 'app_item_meta_data',
},
),
migrations.AddField(
model_name='itemmetadata',
name='item',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app.item'
),
),
migrations.AddField(
model_name='itemmetadata',
name='field'
field=models.CharField(max_length=80)
),
migrations..AddField(
model_name='itemmetadata',
name='field_value',
field=models.CharField(max_length=80)
),
migrations..AddField(
model_name='itemmetadata',
name='created_at',
field = models.DateTimeField(auto_now_add=True)
),
migrations..AddField(
model_name='itemmetadata',
name='modified_at',
field=models.DateTimeField(auto_now=True)
)
If you then start from a empty database without any tables, it will normally put the item_id as second field.
That being said, usually the order of the fields does not matter (that much). Especially if you use the Django ORM to make queries, then Django will ensure that the columns are fetched in the right order to map these to model objects.

Does Rails has a way to validate if the foreign key value exists in an optional relationship?

I am using Rails version 5.1.3.
Does Rails have a way to validate if the foreign key value exists in an optional relationship?
I have the next model:
class Post < ApplicationRecord
belongs_to :category, optional: true
validates :category, presence: true, allow_nil: true
end
This is the migration:
class CreatePosts < ActiveRecord::Migration[5.1]
def change
create_table :posts do |t|
t.string :name, limit: 100
t.references :category, foreign_key: true, null: true
t.timestamps
end
end
end
These are the cases:
# Case 1
p1 = Post.new({})
p1.save #Working as I expected ... record Inserted
# Case 2
p2 = Post.new({category_id: 3}) # A category with id 3 exists
p2.save #Working as I expected ... record Inserted
# Case 3
p3 = Post.new({category_id: 30}) # A category with id 30 does not exists.
p3.save # Not working as I expected
In the case 3 I was expecting an Active Record validation error something like This Category does not exists but instead of I get
an SQL message:
INSERT INTO "posts" ("category_id", "created_at", "updated_at") VALUES (?, ?, ?) [["category_id", 10], ["created_at", "2017-09-26 00:59:47.645185"], ["updated_at", "2017-09-26 00:59:47.645185"]]
ActiveRecord::InvalidForeignKey: SQLite3::ConstraintException: FOREIGN KEY constraint failed: INSERT INTO "posts" ("category_id", "created_at", "updated_at") VALUES (?, ?, ?)
This could help:
validates :category,
presence: true,
if: -> { category_id.present? }
The database is enforcing foreign key integrity, which just means that it will not allow you to associate things to another thing that does not exist. This is expected behavior.
If you want to do something if the association doesn't exist, you should do a find first before attempting to create the association.
The optional validation you set up for the foreign key relationship just means that a relationship is not required. In other words, you can create posts that are not associated with categories. However, that does not mean the database will be happy if you attempt to form an association with categories that do not exist. Rails is cool whether or not you provide an association or not, but the moment it attempts to do something that violates rules set by the database, then the database has every right to complain!

Relationship between student, enrollment, course and section table

I want to get record student enroll the specific course in which section ???
Because one course taught in multiple sections.
class Student < ActiveRecord::Base
has_many :enrolments
has_many :courses, through: :enrolment
end
class course < ActiveRecord::Base
has_many :enrolment
has_many :students, through: :enrolment
has_many :sections
end
class enrolment < ActiveRecord::Base
belongs_to :students
belongs_to :courses
end
class Section < ActiveRecord::Base
belongs_to :courses
end
Relationship
Student.first.courses.first.sections
Due to this relationship i got multiple sections.
But I want to get single section in which student enroll the specific course ???
Here I am confused do you think this design is good? If not please provide some suggestions.
You need a table/class with (student, course, section) where a articular student is enrolled in particular section of a particular course, or with (enrolment, section) where a pair satisfies the relationship when the enrolment student is enrolled in the enrolment section--which from your remarks is probably not how you are using the enrolment & sections of class Class. (Ask yourself: how would you record that a particular student is enrolled in a particular section of a course?) Start with Student, Class & Section; add Enrolled with student-course-section.
This is what I think could work based on Laravel framework.
Tables
Students
Courses
Sections
Table Representation
Students Table
id | name | email |
---|------|-------|
1 |Test |test#test.com|
Courses Table
id | name |
---|------|
1 |Course A|
Sections Table
id | name |
---|------|
1 |Section A|
Relationships
Students <----[M:M]----> Courses
Courses <----[M:M]----> Sections
Relationship Tables
Course_Student - course_id & student_id(PK & FK)
Course_Section - course_id & section_id(PK & FK)
Course_Student Table
course_id | student_id|
----------|-----------|
1 |1
Course_Section Table
course_id | section_id
----------|-----------
1 |1
Now you can get student_id, course_id and section_id based on Weak entities or Intermediate tables(as per Laravel).
Hope this helps you

Is it possible to display data through a id?

My apologies for the abstract title. I'm in the proces of adding a friendship feature in my Rails/Angular application following #163 Self-Referential Association.
With the help of some other users here I've created the option to add friends, but I'm having some trouble displaying the correct data (name, etc) in a friendslist.
I have a friendships table,
create_table "friendships", force: :cascade do |t|
t.integer "user_id"
t.integer "friend_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
When a friend is added it creates a new record in the table with a user id and a friend id.
{"id":1,"user_id":1,"friend_id":2,"created_at":"2015-12-29T13:18:44.499Z","updated_at":"2015-12-29T13:18:44.499Z"},
{"id":2,"user_id":1,"friend_id":3,"created_at":"2015-12-29T13:18:45.463Z","updated_at":"2015-12-29T13:18:45.463Z"},
{"id":3,"user_id":1,"friend_id":4,"created_at":"2015-12-29T13:18:46.420Z","updated_at":"2015-12-29T13:18:46.420Z"}
In the friendships_controller.rb I have,
def index
friend = current_user.friendships
render :json => friend.to_json()
end
So I can see what friend_id is connected to a user_id. But if I want to display the friends in a ng-repeat I'm going to need more data.
The friend_id is the added users id. So if a user adds the 4th user to his friendslist the friend_id would be 4. So it possible to connect that friend_id to the user_id so I can then show the {{ user.name }} or do I need to add all the data like name, birthday etc to the parameters of the friend create function?
So for example I would add the friends name to the parameters so the JSON looks like this,
{"id":1,"user_id":1,"friend_id":2,"name":john,"created_at":"2015-12-29T13:18:44.499Z","updated_at":"2015-12-29T13:18:44.499Z"},
{"id":2,"user_id":1,"friend_id":3,"name":david,"created_at":"2015-12-29T13:18:45.463Z","updated_at":"2015-12-29T13:18:45.463Z"},
{"id":3,"user_id":1,"friend_id":4,"name":cash,"created_at":"2015-12-29T13:18:46.420Z","updated_at":"2015-12-29T13:18:46.420Z"}
Then I can do something like ng-repeat friend in friendships {{ friend.name }}

How to make column data unique for each user_id in ruby on rails

The problem is that I have a table customers with some customers related columns like customersID.
Also I have a column user_id So that the customers data only relate to one user.
class Customers
belongs_to :user
end
class Users
has_many :customers
end
Now I have the :unique on the customersID. But this makes every customerID unique all over the table.
What i want is that the customerID is unique per user_id.
Any idea or suggestions?
Edit: Question seems bit unclear.
I have a table users
user1
user2
user3
also i have a table customers where each customer get a user_id from the user who created him. The user can input a customerID, which should be unique for each user.
customerID=1 user_id1
customerID=2 user_id1
customerID=1 user_id3
customerID=3 user_id1
customerID=1 user_id2
...
I crud the customers data via #customers = current_user.customers in my CustomersController. The customerID is a simple t.integer "customerID"
It seems as if you want a Customer to have a User, is that correct? Try something like this:
class Customer
has_one :user
end
class User
belongs_to :customer
end
That way you can have a customer tied to one specific user_id. Then you could do something like this:
#customer = Customer.where(name: "company name").first
#user = #customer.user #this will find the customer's unique user
Why not force uniqueness of the pair Customer_id, iser_id via an index on your Customers table ? You could create a migration something like
add_index_to_costumers.rb
class AddIndexToCustomers < ActiveRecord::Migration
def change
add_index :customers, [:customerID,:user_id], unique: true
end
end
It should ensure at the database level that the pair customerID/user_id is unique. If I well understand your question it's what you expect.
Hope this helps
Cheers

Resources