yesodweb / persistent

Persistence interface for Haskell allowing multiple storage methods.
MIT License
467 stars 297 forks source link

foreign primary key to foreign primary key #806

Open gurgl opened 6 years ago

gurgl commented 6 years ago

In the below example (disregard User, its just a simple auto incremnt entity) i want to express GroupTopicMember's pk is a foreign key to GroupTopic that in turn has pk that is a foreign key to ChatTopic. It needs to be that chain cause there can be no GroupTopicMember if theres no GroupTopic. When compiling this program i get

• Not in scope: type constructor or class ‘ChatTopicId’


ChatTopic
  ct_name String sql=name
  deriving Show Eq Generic

GroupTopic
  pcm_topicId ChatTopicId sql=topic_id
  Primary pcm_topicId
  deriving Show Eq Generic

GroupTopicMember
  prtm_userId UserId sql=user_id
  prtm_topicId GroupTopicId sql=topic_id
  Primary prtm_userId prtm_topicId
  deriving Show Eq Generic

...as if its not yet aware of ChatTopicId when it tried to construct the GroupTopicMemberKey? If i change prtm_topicId to ChatTopicId it compiles but the resulting migration created foreign key goes from GroupTopicMember to ChatTopic - which is not correct association. Im using mysql, if that explains anything. (Note, there are more "xyz-chat-topic" entities not shown here - but these have direct association to ChatTopic and doesnt have associated entities, so works wo problem. ChatTopic is conceptually a sumtype, but dont know if I could model such using Persistent)

naushadh commented 6 years ago

methinks your issue is Primary pcm_topicId within the declaration of GroupTopic. <TypeName>Id is only generated when the table has an auto-increment id column (ex: ChatTopic). The moment you declare a natural key like Primary pcm_topicId, you loose the generation of GroupTopicId.

This would give you GroupTopicId, but now you end up with an id column in addition to the pcm_topicId column.

ChatTopic
  ct_name String sql=name
  deriving Show Eq Generic

GroupTopic
  pcm_topicId ChatTopicId sql=topic_id
- Primary pcm_topicId
+ UniqueTopics pcm_topicId
  deriving Show Eq Generic

GroupTopicMember
  prtm_userId UserId sql=user_id
  prtm_topicId GroupTopicId sql=topic_id
  Primary prtm_userId prtm_topicId
  deriving Show Eq Generic

However I think you instead want to FK from GroupTopicMember (prtm_topicId) into the natural key of GroupTopic. You can do so like this:

ChatTopic
  ct_name String sql=name
  deriving Show Eq Generic

GroupTopic
  pcm_topicId ChatTopicId sql=topic_id
  Primary pcm_topicId
  deriving Show Eq Generic

GroupTopicMember
  prtm_userId UserId sql=user_id
- prtm_topicId GroupTopicId sql=topic_id
+ prtm_topicId ChatTopicId sql=topic_id
  Primary prtm_userId prtm_topicId
+ Foreign GroupTopic fkparent pcm_topicId
  deriving Show Eq Generic

Checkout the entity syntax guide, it explains foreign-keying columns, composite keys, sum-types etc in detail: https://github.com/yesodweb/persistent/blob/master/docs/Persistent-entity-syntax.md

P.S. Not sure if mixing <TypeName>Id and Foreign place nice together in making your desired outcome. This is what I gathered from the docs.