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:

http://gist.github.com/322710

Ready for the follow-up :slight_smile:

Cheers,

Andy

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 Smith 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.

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.

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

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

Andy Jeffries 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?

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

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

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.