Question

I'm using DataMapper in Rails, replacing ActiveRecord and am trying to do some single table inheritance. The problem is, sqlite3 seems to be reading all of my inherited tables weird, and so I get some strange errors when trying to create an instance of that table in ruby.

I am using a model, ServerFile, to represent any uploaded or generic file that I want instanced in my database. I have another model, Upload, that extends that and represents an Upload from a user. I have two more models extending from ServerFile, Thumbnail, and UploadThumbnail, to represent a generic Thumbnail and a Thumbnail for an upload accordingly.

The error I keep getting is DataObjects::IntegrityError (server_files.upload_id may not be NULL) when I try to create an instance of an Upload like this:

upload = Upload.new(
    filename: uploaded_io.original_filename, 
    path: path.to_s, 
    content_type: uploaded_io.content_type, 
    token: rand_token())

upload.member = @member
upload.title = params[:title]
upload.description = params[:description]
upload.save

And here are my models:

class ServerFile
    include DataMapper::Resource
    property :id, Serial
    property :token, String, unique_index: true
    property :filename, String
    property :path, Text, unique: true
    property :content_type, Text, length: 5..200
    property :type, Discriminator

    property :created_on, Date
    property :created_at, DateTime
    property :updated_on, Date
    property :updated_at, DateTime
end

class Upload < ServerFile
    property :title, String
    property :description, Text

    has n, :topics, through: Resource
    has n, :subjects, through: Resource
    has n, :downloads
    has n, :comments, 'UploadComment'
    has n, :ratings, 'UploadRating'

    belongs_to :member
    has 1, :thumbnail, 'UploadThumbnail', required: false
end

class Thumbnail < ServerFile
    @@IMAGE_EXTENSIONS = [:'png', :'jpg', :'jpeg', :'gif', :'svg', :'cgm']
    validates_with_method :filename, :is_valid_image?

    def is_valid_image?
        @@IMAGE_EXTENSIONS.each do |ext|
            return true if /[\w\d\.\_\-]+\.#{ext.to_s}/ =~ @filename
        end
        [false, 'Invalide image type.']
    end
end

class UploadThumbnail < Thumbnail
    belongs_to :upload
end

And here is my sqlite schema for the table 'server_files' (and btw, when I list my tables, 'uploads' isn't listed among them):

sqlite> .schema server_files
CREATE TABLE "server_files" ("id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "token" VARCHAR(50), "filename" VARCHAR(50), "path" TEXT, "content_type" TEXT, "type" VARCHAR NOT NULL, "created_on" DATE, "created_at" TIMESTAMP, "updated_on" DATE, "updated_at" TIMESTAMP, "title" VARCHAR(50), "description" TEXT, "member_id" INTEGER NOT NULL, "upload_id" INTEGER NOT NULL);
CREATE UNIQUE INDEX "unique_server_files_path" ON "server_files" ("path");
CREATE UNIQUE INDEX "unique_server_files_token" ON "server_files" ("token");
Was it helpful?

Solution 2

DataMapper for some reason just didn't like my superclass ServerFile. When I broke it up, it worked perfectly! (:

class Upload < ServerFile
    include DataMapper::Resource
    property :id, Serial
    property :token, String, unique_index: true
    property :filename, String
    property :path, Text, unique: true
    property :content_type, Text, length: 5..200

    property :created_on, Date
    property :created_at, DateTime
    property :updated_on, Date
    property :updated_at, DateTime
    property :title, String
    property :description, Text

    has n, :topics, through: Resource
    has n, :subjects, through: Resource
    has n, :downloads
    has n, :comments, 'UploadComment'
    has n, :ratings, 'UploadRating'

    belongs_to :member
    has 1, :thumbnail, 'UploadThumbnail', required: false
end

class Thumbnail < ServerFile
    include DataMapper::Resource
    property :id, Serial
    property :token, String, unique_index: true
    property :filename, String
    property :path, Text, unique: true
    property :content_type, Text, length: 5..200
    property :type, Discriminator

    property :created_on, Date
    property :created_at, DateTime
    property :updated_on, Date
    property :updated_at, DateTime

    @@IMAGE_EXTENSIONS = [:'png', :'jpg', :'jpeg', :'gif', :'svg', :'cgm']
    validates_with_method :filename, :is_valid_image?

    def is_valid_image?
        @@IMAGE_EXTENSIONS.each do |ext|
            return true if /[\w\d\.\_\-]+\.#{ext.to_s}/ =~ @filename
        end
        [false, 'Invalide image type.']
    end
end

class UploadThumbnail < Thumbnail
    belongs_to :upload
end

OTHER TIPS

There is no need for an upload_id column on the server_files table. Because Upload inherits from ServerFile, this would essentially be self-referential. The column type with type Discriminator will be set to Upload in the database when the model is successfully saved. That being said, if you want to set a custom upload_id anyways, you can do it with a callback like this:

before :create, :generate_upload_id

def generate_upload_id
  generated_upload_id = <do something>
  attribute_set(:upload_id, generated_upload_id) unless upload_id
end

Otherwise, simply write a migration that removes the upload_id column from the server_files table

Source: http://datamapper.org/docs/misc.html

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top