I'm writing an image gallery.
An image in the gallery belongs to a project. Each project can have a parent project. An example would beproject1 > project2 > project3 > image1
To allow the admins to use FTP i wanted to have 'pretty' paths to the files.
Thumbnails and previews are stored inROOT/previews/<image-id>
and ROOT/thumbnails/<image-id>
while images are stored in ROOT/images/<project-name>/<project-name>/.../<image-name>
The admin can then simple upload image to the correct folder and the application would discover them and handle everything.
This of course causes the problem of having to fetch all parents when getting the path to an image. Currently this means first fetching the image, then it's project, then that projects parent and so on.
If it was always one ancestor (nesting level) the problem would be easy, it's the 0 or more that's the problem.I don't expect the projects to be very deeply nested but I'm still concerned as having many queries per request is bad.
Is there any way I could avoid this? Maybe fetch all in one?
Maybe I should skip having FTP uploads all together and only allow drag and drop from the UI?Maybe I'm worrying too much, I'm only constraining the queries by an indexed field
My schema.rb
currently looks like this:
ActiveRecord::Schema.define(:version => 20110827230616) do
create_table "images", :force => true do |t|
t.string "name"
t.string "filename"
t.text "info"
t.integer "project_id"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "images", ["project_id", "filename"], :name => "index_images_on_project_id_and_filename", :unique => true
create_table "projects", :force => true do |t|
t.string "name"
t.text "info"
t.string "dates"
t.integer "parent_id"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "projects", ["parent_id", "name"], :name => "index_projects_on_parent_id_and_name", :unique => true
end
image.rb
class Image < ActiveRecord::Base
belongs_to :project
# Validation
validates_presence_of :project_id, :filename
attr_readonly :filename
def path
if not self.project_id or not self.project
raise "Called path on an image without a project"
end
return File.join(self.project.path, self.filename)
end
def thumbnail_path
raise "Called thumbnail_path on an image without an id" unless self.id
return File.join(Rails.application.config.thumbnail_directory, self.id.to_s)
end
def preview_path
raise "Called preview_path on an image without an id" unless self.id
return File.join(Rails.application.config.preview_directory, self.id.to_s)
end
def exists?
if not File.file? self.path
return false
end
return true
end
end
project.rb
class Project < ActiveRecord::Base
belongs_to :parent, :class_name => 'Project'
has_many :children, :class_name => 'Project',
:dependent => :destroy, :foreign_key => 'parent_id'
has_many :images, :dependent => :destroy
def safe_name
return self.name.downcase.gsub(/\s/, '-').gsub(/[^a-z0-9-]/, '_')
end
def path
tmp = self
parents = []
while tmp
parents.insert(0, tmp.safe_name)
tmp = tmp.parent
end
return File.join(Rails.application.config.image_directory, parents)
end
end
Example request can look like this:
Started GET "/images/1" for 127.0.0.1 at Sat Sep 03 10:23:30 -0700 2011
Processing by ImagesController#show as HTML
Parameters: {"id"=>"1"}
Image Load (0.2ms) SELECT "images".* FROM "images" WHERE "images"."id" = ? LIMIT 1 [["id", "1"]]
Project Load (0.2ms) SELECT "projects".* FROM "projects" WHERE "projects"."id" = 2 LIMIT 1
Project Load (0.2ms) SELECT "projects".* FROM "pro开发者_Python百科jects" WHERE "projects"."id" = 1 LIMIT 1
Project Load (0.1ms) SELECT "projects".* FROM "projects" WHERE "projects"."id" IS NULL LIMIT 1
You could use a nested set model for storing the projects. Using it you could simply get the project, then get all the parents of the project in one query.
Wikipedia about nested sets:
http://en.wikipedia.org/wiki/Nested_set_model
Rails plugin for nested sets:
https://github.com/collectiveidea/awesome_nested_set
精彩评论