Modelling Question

Let’s say I have the following real life objects Books, Authors, Roles.
A
Book can have one or many Authors and an Author can have one or many
Books.
An Author can also have one or many roles associated with a book (maybe
they’re the author, or co-author, or commentator to whatever)

How would you model that?

I have a similar example and have asked for advice on this list but no
responses so I thought I’d change tact. ~The winner to solving this
problem,
gets my follow up question :wink:

CIA

-Ants

I would do:

Book
has_many :authorings
has_many :authors, :through => :authorings

Authoring
belongs_to :book
belongs_to :author
has_many :authoring_roles
has_many :roles, :through => :authoring_roles

AuthoringRole
belongs_to :authoring
belongs_to :role

Role
has_many :authoring_roles
has_many :authorings, :through => :authoring_roles

Author
has_many :authorings
has_many :books, :through => :authorings

So, an ‘authoring’ encapsulates the concept of an author working on a
book, and during that authoring process the roles they fulfilled are
encapsulated in authoring_roles.

Generally, it’s good to approach this not from a data point of view, but
from a real world concepts point of view. The models should always try
to encapsulate a real world concept, and have a name which describes
that concept as simply as possible.

Book
Authoring
AuthoringRole
Role
Author

I did this in 3 models, I don’t know who models the OP’s requirements
more
closely, but for now here’s my modelling:

Ready for the follow-up :slight_smile:

Cheers,

Andy

Also, your system doesn’t easily let you attach properties or behaviour
to a given role. Presumably you would have a field ‘name’, or something
similar, in the roles table, and you could hang behaviour on the value
of the name but again it’s clumsy and messy and fragile, and creates a
lot of repetition. It’s not normalised.

On 5 March 2010 13:18, Andy J. [email protected] wrote:

Book
Authoring
AuthoringRole
Role
Author

I did this in 3 models, I don’t know who models the OP’s requirements more
closely, but for now here’s my modelling:
http://gist.github.com/322710

You may have squashed it into fewer models, but the relationships do
not map the real world relationships intuitively. You have that a
Book has many Roles, whereas in the real world a book does not have
roles at all, it is the author that has the roles in association with
a particular book.

Also by using a string for the role_type you will have many Role
records with “MAIN_AUTHOR” for example. If you later decided that
“Primary Author” would be better you would have to change the string
in many records.

Colin

Hi Andy - why not just post your code here? Saves clicking away, and
it’s easier to discuss the code. Anyway, you posted

class Role < ActiveRecord::Base
belongs_to :author
belongs_to :book
end

class Book < ActiveRecord::Base
has_many :authors, :through => :roles
has_many :roles
end

class Author < ActiveRecord::Base
has_many :books, :through => :roles
has_many :roles
end

The problem with this is this part of the description: "An Author can
also have one or many roles associated with a book " - with “or many”
being the key part.

In your system, let’s say that David S.h is the editor of and a
contributor to a book called “Advances in web design”. Then you have
two roles objects linking david to the book. That makes it look like
two people worked on the book, who happen to be the same person, and it
makes it look like david wrote two books that happen to be the same
book. This is confusing. If i want to know, for example, how many
books David has worked on, i’d expect the answer to be 1, not 2. You
could get around this by calling ‘uniq’ on the results of .books or
.authors, but it’s unintuitive and fragile.

Andy J. wrote:

Also by using a string for the role_type you will have many Role
records with “MAIN_AUTHOR” for example. If you later decided that
“Primary Author” would be better you would have to change the string
in many records.

I’m not saying MAIN_AUTHOR has to be displayed anywhere, display logic
is
down to the view/helpers, but it’s often easier skimming through
database
records with a name for type rather than an integer you then need to
lookup
separately or remember to join.

Cheers,

Andy
Rails finds it easier to use foreign keys based on id. Are we talking
about rails or about a person manually searching the database using
their hands and eyes?

Also by using a string for the role_type you will have many Role
records with “MAIN_AUTHOR” for example. If you later decided that
“Primary Author” would be better you would have to change the string
in many records.

I’m not saying MAIN_AUTHOR has to be displayed anywhere, display logic
is
down to the view/helpers, but it’s often easier skimming through
database
records with a name for type rather than an integer you then need to
lookup
separately or remember to join.

Cheers,

Andy

Rails finds it easier to use foreign keys based on id. Are we talking
about rails or about a person manually searching the database using
their hands and eyes?

A person manually scanning the database (in a MySQL command prompt).

If this was actually a useful table (as opposed to just id|name in the
RoleType table) and you were searching from it then I agree, but I’m all
for
simplest solutions until they need refactoring to support something else
(so
in this case I’d happily go with the text field until you needed to
start
having other attributes than just a lookup from id to name).

Cheers,

Andy

I don’t think you could do all of this with the standard
nested_attributes on the book model, you’d need to write your own setter
method. Ultimately you’re going to be creating an authoring_role
object, and either finding an existing author or creating a new author,
and the same for the role. So, if the authoring_role model has
nested_attributes that let you set role and author data, then you could
call that when you create it. I’ve not done much with nested_attributes
myself though.

Sorry for the delay in getting back on this but I’ve just got back from
a
weekend away.

Okay, so Max, you and I agree on the modelling for this concept so that
starts to narrow down where my problem is.

To begin with, I thought my problem was because I had a join model
acting as
the parent to another join model and maybe the primary key of Authorings
wasn’t being created before the join to AuthoringRole was being made.

Where we disagree is in the relationships and so this is obviously where
my
problem lies.

I agree with all your relationships but I’ve added a bit extra … In
my
book.rb, I have included accepts_nested_attributes_for :authors,
:authoring_roles
and also have a has_many :authoring_roles, :through => :authorings

I’ve done this because I want to accept all that information when
creating a
book. when I say @book.save, I want it to include the roles as well as
the
authors.

So how should I set that up? In my real world example Book == Account,
Author == Member and Role is one or many of Account Contact/Swimmer/etc.

When creating a new Book (Account), I am creating a new Author (Member)
and
so the form accepts_nested_attributes_for :authors, :authoring_roles (in
my
obviously mixed up logic)

If I have a form where I accept Book, Author and Role information and
then
want to create/update the Book, Author, Authorings and AuthoringRoles
through @book,save, how to go about it?

I’ll have a play with this now and if I can break myself out of my cycle
and
crack it, I’ll get in touch. If you/someone provides me the solution
before
that, I’d be grateful as this has been sending me mad.

Merci

-Ants

On 5 March 2010 14:10, Max W. [email protected] wrote:

has_many :roles, :through => :authoring_roles
has_many :authorings

http://groups.google.com/group/rubyonrails-talk?hl=en.


100% naturally selected. 0% designed.