Reputation: 485
I am looking to add tags to my Blog app.
I wish for Posts to have none, one or many tags. For now, I want to restrict the choice of tags to 10 pre-defined tags.
Should I create a model:
post_id, tag_name
1, sport
1, news
1, fun
2, sport
3, news
Or a wider table:
post_id, tag_sport, tag_special
1,Yes,No,...
Or include the chosen tags within the post table:
post_id, post_body, post_tags
1, lorem ipsum, sport|news|fun
How would the form look?
Free textbox separated by commas? Checkbox?
I know there are plugins and existing options available, but would like to be able to control the tags via a table. I have followed some tutorials but they do not provide a complete solution.
Upvotes: 1
Views: 2887
Reputation: 1071
My suggestion will be use habtm
relation between books and tags.
class < books
has_and_belongs_to_many :tags
validates :no_of_tags
def no_of_tags
if self.tags.length < 4 #your choiche
false
end
end
end
class < tags
has_and_belongs_to_many :books
end
Plus create a table books_posts
this will solve your problem
Upvotes: 0
Reputation: 6567
Personally, and keeping with "the simplest thing that could possibly work", if you only have to deal with, say, 10 pre-defined tags then I might be inclined to go with a simple serialized field.
However, this entails additional effort and places unnecessary load on your database when you want to search for posts by tag (simplest is a LIKE query, which in most cases involves a table scan).
So, personally, even with a simple tagging system I will almost always end up with a tags
table of just id
and name
, then a has_and_belongs_to_many
relationship between the tags and the taggable entities:
class Post < ActiveRecord::Base
has_and_belongs_to_many :tags
And in Tag
:
class Tag < ActiveRecord::Base
attr_accessible :name
has_and_belongs_to_many :posts
validates :name, presence: true, uniqueness: {case_sensitive: false}
Of course, this means having to write a migration to create the appropriate join table, something like:
class CreatePostsTags < ActiveRecord::Migration
def change
create_table :posts_tags do |t|
t.references :post, null: false
t.references :tag, null: false
end
end
end
This way the database schema is also kept in normal form.
To search by tag, it reduces to something like:
Tag.find_by_name('news').posts
(In practice, you'll want a Tag.named(name)
method that'll strip.downcase
name
for you.)
As for the form, again, the simplest thing I might end up with is just a plain text field, where tags are separated by commas (and therefore the comma character isn't a valid character in a tag name). Or, if you don't allow spaces in tag names, you can use the space as a delimiter.
Upvotes: 0
Reputation: 3085
You won't get a comnplete solution here. SO is not a code dispenser.
What you have here is a typical n to m relationship. A post may have many tags (the restriction of a max of 10 tags is irrelevant for this) - and a tag can be part of many posts.
n to m relations are modelled through the use of a 2 additional tables: 1 for the relating model (the tag) and one for saving the relationships.
This give you (assuming your post table is named posts)
Table posts_tags
which looks like this:
id
post_id
tag_id
And a table tags
:
id
tagname
The relation is then described in the models via has_and_belongs_to_many
If a post only can have one tag - you can simplify this with using just a 1 to many relation.
For the design and given the small number of tags, I would just use a bunch of checkboxes. But that is up to you. You will find examples for the visualisation of n:m with rails on the web.
Upvotes: 0
Reputation: 6942
You can try this acts-as-taggable-on gem,watch railscast may give you better idea
Upvotes: 1