I'm using DataMapper in a simple application to track sales. I have a Day class, like this:
class Day
include DataMapper::Resource
property :id, Serial, :key => true
property :date, DateTime
property :bestseller, String
property :total_money, Decimal
property :total_sold, Integer
property :total_orders, Integer
has n, :sales
end
and a Sales class:
class Sale
include DataMapper::Resource
belongs_to :day
property :id, Serial, :key => true
property :name, String
property :amount, Integer
property :value, Integer
end
When trying to add a new Sale to the database, like so:
s = Sale.new(:day => Day.get(1), :name => "Foo", :amount => "42", :value => "42"
I get this error when calling save.
DataObjects::IntegrityError at /sell
sales.date may not be NULL
I have no date property in Sale, so I'm not sure where this is coming from. First I thought that the Day object I'm getting doesn't have a day set, so I did d = Day.get(1).date = Time.now and saved it, but this doesn't resolve the error.
What did I break?
EDIT The sqlite3 schema
CREATE TABLE "sales" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"name" VARCHAR(50),
"amount" INTEGER,
"value" INTEGER,
"day_id" INTEGER NOT NULL,
"drink_id" INTEGER NOT NULL
);
CREATE INDEX "index_sales_day" ON "sales" ("day_id");
CREATE INDEX "index_sales_drink" ON "sales" ("drink_id");
I think I fixed it. Apparently, I had an old date property at one point in Sale. I entered the ruby interpreter, required my model and used DataMapper.auto_migrate! to reset the entire database. This fixed the problem.
Related
I am building a small app and at this point I am creating the database schema. I use PostgreSQL with Sequel and I have the two migrations:
Sequel.migration do
change do
Sequel::Model.db.run 'CREATE EXTENSION IF NOT EXISTS "uuid-ossp"'
create_table :user do
String :id, :type => :uuid, :primary_key => true, :default => Sequel.function(:uuid_generate_v4)
DateTime :created_at
DateTime :updated_at
index :id, :unique => true
end
end
end
Sequel.migration do
change do
Sequel::Model.db.run 'CREATE EXTENSION IF NOT EXISTS "uuid-ossp"'
create_table :location do
String :id, :type => :uuid, :primary_key => true, :default => Sequel.function(:uuid_generate_v4)
foreign_key :user_id, :user, :type => :uuid
String :name, :unique => true, :null => false # TODO: unique, per user
Float :latitude, :null => false
Float :longitude, :null => false
DateTime :created_at
DateTime :updated_at
index :id, :unique => true
full_text_index :name, :index_type => :gist
end
end
end
As you can see the name column on the location table is unique, but I am in doubt about it. Because I want to establish a unique constraint on the name, but per user, so a user can have only one location with the same name but in the entire table many users can have locations with the same name (the relation between user and location is a one to many).
I suppose my unique constraint added on the column will make column generally unique, so amongst all users the location name must be unique. If so, how do I create a constraint that makes the name unique on a per user basis?
Just create the unique constraint over both columns:
UNIQUE (user_id, name)
Per documentation:
This specifies that the combination of values in the indicated columns
is unique across the whole table, though any one of the columns need
not be (and ordinarily isn't) unique.
But from the looks of it, you really want another table user_location than implements an n:m relation between locations and users - with a primary key on (user_id, location_id).
And don't call the first table "user", that's a reserved word in standard SQL and in Postgres and shouldn't be used as identifier.
I have an app that tracks a player's progress over a fitness program. So every player has multiple weeks with the same :week_id
Week_id combined with the belongs_to relationship is the composite primary key for the Week record.
However, when I try to create two weeks with the same week_id that belong to different players, I get a "column week_id is not unique" error.
I feel like I am on the right track because when I want to fetch a week record, it tells me that I need two arguments to get it - the week_id and the player_id.
I am probably missing something simple here. I hope you can show me.
require "rubygems"
require "json"
require "sqlite3"
require "data_mapper"
require "bigdecimal"
DataMapper::setup(:default, "sqlite3://#{Dir.pwd}/prod.db")
DataMapper::Model.raise_on_save_failure = true
class Player
include DataMapper::Resource
property :name, String, :key => true
property :age, Integer
has n, :weeks
end
class Week
include DataMapper::Resource
property :week_id, Integer, :key => true
property :score, Integer
belongs_to :player, :key => true
end
DataMapper.finalize.auto_migrate!
#jack = Player.create(:name => "jack")
#jack.weeks.create(:week_id => 1)
#jill = Player.create(:name => "jill")
#jill.weeks.create(:week_id => 1)
Looks like you already came up with a solution, but I'll go ahead and post another answer for posterity, since I ran into the same issue.
It seems like declaring ':player_name' as a property that is a key as well as with the 'belongs_to' declaration, you end up with the same SQL to create the table, but it also recognizes that week_id is part of a composite key and does not need to be unique on its own. (It appears DataMapper has issues with composite keys split across 'property' and 'belongs_to' declarations.)
class Week
include DataMapper::Resource
property :week_id, Integer, :key => true
property :player_name, String, :key => true
property :score, Integer
belongs_to :player, :key => true
end
Results in the following SQL to create the table:
~ (0.000000) CREATE TABLE "weeks" ("week_id" INTEGER NOT NULL, "player_name" VARCHAR(50) NOT NULL, "score" INTEGER, PRIMARY KEY("week_id", "player_name"))
And your example code works without errors
So I ended up making a small change to my Week model like so
class Week
include DataMapper::Resource
property :id, String, :key => true
property :week_id, Integer
property :score, Integer
belongs_to :player
end
And whenever I create a new Player model I concatenate the foreign key and the week_id to create the id string
So my id string will look something like "jack1" for the first record for a player with an :id of "jack"
Not sure if this is the data_mapper way but it works.
I'm quite new to mongodb and I'm using sinatra and mongomapper to update the values of an embedded document with the following set up:
class TeamMember
include MongoMapper::Document
key :name, String, :required => true
many :team_member_projects
end
class TeamMemberProject
include MongoMapper::EmbeddedDocument
key :date, Date, :required => true
one :project
end
class Project
include MongoMapper::Document
key :name, String, :required => true
end
The modifier code is:
team_member = TeamMember.find(params[:team_member])
project = Project.find(params[:project])
date = Date.parse(params[:date])
tm_project = TeamMemberProject.new(:project => project, :date => date)
team_member.push(:team_member_projects => tm_project)
team_member.save
but I get the error for .push line:
BSON::InvalidDocument at /project/add
Cannot serialize an object of class TeamMemberProject into BSON.
Did I not declare my embedded document properly? Or is there another way to update embedded documents, I don't know about. I'm trying to use: http://mongomapper.com/documentation/plugins/modifiers.html#push
This seems to work
team_member = TeamMember.find(params[:team_member])
project = Project.find(params[:project])
date = Date.parse(params[:date])
tm_project = TeamMemberProject.new(:project_id => project.id, :date => date)
team_member.team_member_projects << tm_project
team_member.save
It seems like I have to use project.id. Not sure why. Also not sure why my .push doesn't work, as I would have assumed it does the same thing as <<.
How to use list/array as property in DataMapper on Jruby on Google AppEngine?
This will work like has..and..belongs..to..many, without a join table...
class Person
include DataMapper::Resource
property :id, Serial
property :name, String, :nullable => false
property :project_ids, List
timestamps :at
# project should be flagged as archived, not deleted
def projects
Project.all(:id => project_ids)
end
end
class Project
include DataMapper::Resource
property :id, Serial
property :name, String, :nullable => false
property :archived, Boolean, :default => false
# the join table is bolted onto the person model
def people
Person.all(:project_ids => id)
end
end
In Datamapper, how would one specify the the combination of two fields must be unique. For example categories must have unique names within a domain:
class Category
include DataMapper.resource
property :name, String, :index=>true #must be unique for a given domain
belongs_to :domain
end
You have to create a unique index for the two properties:
class Category
include DataMapper::Resource
property :name, String, :unique_index => :u
property :domain_id, Integer, :unique_index => :u
belongs_to :domain
end
Actually, John, Joschi's answer is correct: the use of named :unique_index values does create a multiple-column index; it's important to read the right-hand side of those hash-rockets (i.e., if it had just been true, you would be right).
Did you try to define both properties as keys? Not sure I have tried it but that way they should become a composite key.
property :name, String, :key => true
property :category, Integer, :key => true