How do I define a composite key in Persistent - haskell

How do I declare to Persistent that I have a table the primary key of which is a combination of two fields?
For example, assume I have a table containing first_name and last_name, then in SQL syntax I'll need something like:
CONSTRAINT pk_PersonID PRIMARY KEY (first_name,last_name)
Thanks,

You can use the Primary <field1> <field2> syntax as per code below.
PrimaryCompositeWithOtherNullableFields
foo String maxlen=20
bar String maxlen=20
baz String Maybe
Primary foo bar -- THIS LINE --
deriving Eq Show
enter code here
The above code is taken from one of the tests at https://github.com/yesodweb/persistent/blob/master/persistent-test/src/CompositeTest.hs#L74
This wiki page explains the different syntax for defining the model in persistent. It really should have been part of the Yesod book.

See http://www.yesodweb.com/book/persistent, section Uniqueness
Person
firstName String
lastName String
age Int
PersonName firstName lastName
deriving Show
This define a unique key made of both firstName and lastName.

Related

How do I store Either (Key a) (Key b)?

I have the following model:
User
...
Group
...
Sharing
objectId (Either UserId GroupId)
In Sharing entity I want to store either UserId or GroupId and differentiate between them. Simply using Either doesn't work:
Not in scope: type constructor or class `UserId'
Not in scope: type constructor or class `GroupId'
Adding a new sum-type also doesn't work:
data SharingIdType = SharingUserId UserId | SharingGroupId GroupId
Not in scope: type constructor or class `SharingIdType'
Moving SharingIdType into another module isn't possible, because it uses UserId and GroupId types. The only way I see is to create an entity for each sharing type, like UserSharing/GroupSharing.
Other than that, how to approach this problem?
After searching for some time and thinking about it I concluded there are two possible solutions:
1.
If number of SharingIdTypes is static or rarely changes (means, it is OK to recompile the source to change it or alter the DB schema), the proper way to handle the problem is to have to entities for each sharing type:
User
...
Group
...
UserSharing
userId UserId
GroupSharing
groupId GroupId
Here the "sumness" of the problem is moved to DB queries. Whenever I need to find out with what something shared, I make two selectLists and query two tables instead of one.
2.
If number of SharingIdTypes needs to be altered dynamically, the SharingType entity is needed:
User
...
Group
...
SharingType
description String
Sharing
objectId SharingTypeId
This table is filled up with values corresponding to SharingIdTypes constructors:
do
insert $ SharingType "user"
insert $ SharingType "group"
Now whenever we share something, we refer SharingTypeId.

Yesod: querying `persist` database with a custom primary key

Supposing that I have a SQL table with persist, and that I have a custom Text as primary key instead of the auto-incrementing Int64 key.
For example, my database definition is thus:
share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
TorrentD
infoHash Text
ipAddr [Text]
Primary infoHash
deriving Show
|]
Supposing that I then have a plain Text value, what is the idiomatic way to query the database for a row with a primary key matching my Text value?
runDB $ get $ toSqlKey ("test" :: Text) doesn't work as toSqlKey doesn't support custom primary keys and thus expects expects an Int64.
Creating the key manually and running runDB $ get $ Key $ PersistText ("test" :: Text) doesn't work as it is giving me an error about Key not being in scope (although I do have Database.Persist.Class in my imports).
I've found (the?) (an?) answer. It's not very pretty, but:
get (TorrentDKey {unTorrentDKey = torrentPInfoHash torrent})
works.
The unTorrentDKey is something generated inside the template haskell.
It looks like i'll have to pepper
let primaryKey = TorrentDKey {unTorrentDKey = torrentPInfoHash torrent}
around in my code.

How to simply all "data" definitions having the same kind and name of a field?

I have many "data" which represent sql table in a database.
data User = User { userId :: Int, userName :: String }
data Article = Article { articleId :: Int, articleTitle :: String, articleBody :: String }
-- .......
All of them has the field "id" as a primary key. I wonder, is there any way to get rid of necessity to define it each time for each "data", can I anyhow simplify that? If do this:
class DataTable a where
myId :: Int
it won't change anything, will? I'll still have to define "id" for each data and then implement it for DataTable, in fact it'll make more complex.
Sometimes it's best to separate things.
data Identified a = Identified
{ ident :: !Int
, payload :: a }
Now you can deal with identified things in an entirely uniform way.
In GHC 8.0 with DuplicateRecordFields you can use the same record field name for many data types. This is part of the larger feature of OverloadedRecordFields.
Part 3, MagicClasses, introduces derivable type classes for HasField and UpdateField. This is similar in idea to your DataTable.
If you want a field in your data type then you must declare it in the data type definition. I am not aware of any extensions, other than Template Haskell, which change this.

Define and use a Text column as id in persistent mongodb

I'm trying to define and use a table (AKA collection) with a Text column as the unique id. I'm using the 2.0.2 versions of persistent, persistent-mongodb, persistent-template.
This is my table definition:
let mongoSettings = (mkPersistSettings (ConT ''MongoContext)) { mpsGeneric = False }
in share [mkPersist mongoSettings, mkMigrate "migrateAll"][persistUpperCase|
User id=myid
myid Text
count Int
|]
I can't figure out how to create a Key User from a Text value.
I attempted:
mkUser :: Text -> Key User
mkUser x = UserKey . MongoKey $ x
with compiler error:
Couldn't match type ‘Text’ with ‘ObjectId’
Expected type: Text -> Key User
Actual type: ObjectId -> Key User
Are mongo keys in persistent only allowed to be of type ObjectId? From my examination of the relevant source code, this seems to be the case, but I would love to be wrong. Mongodb certainly allows textual keys outside of persistent. In fact, it allows for any unique thing.
I also tried inserting a row like so:
insert $ User "bob" 0
but the resulting database row has an auto-generated ObjectId in the _id column, instead of "bob".
If I define the schema:
User sql=profiles
_id Text
count Int
then I still have the issue of attempting to define a Key User with a Text value.
This is a new feature in persistent-mongoDB (that was not available when I asked the question), so the answer used to be "No, that's not possible" but is now "Yes, and here is how". It's available as of the 2.0.3 series: persistent-2.0.3, persistent-template-2.0.3, and persistent-mongoDB-2.0.3.
Your schema should have a line: Id <type of _id field> For example:
let mongoSettings = (mkPersistSettings (ConT ''MongoContext)) { mpsGeneric = False }
in share [mkPersist mongoSettings][persistUpperCase|
User
Id Text
age Int
|]
and you insert documents with a specific _id like this:
insertKey (UserKey "alice") $ User 1
Also see the wiki.

disabling fields in formlets/digestive-functors?

With formlets/digestive-functors, I'm trying to figure out how selectively disable fields at run-time. Disabling a field would disable both the showing of the field as well as validation.
Contrived example: Suppose our basic form data type looks like:
data Info =
Info {
favcolor :: String,
deptId :: Int,
company :: String,
agree :: Boolean
}
but the deptId field should only be shown if the user is an employee, otherwise the company field should be shown. And the agree field should be shown if the user hasn't already clicked the "Agree" box.
I don't want to create a different form type for each possible variation: (favcolor, deptId, agree), (favcolor, deptId), (favcolor, company, agree), (favcolor, company). What else can I do?
I think this breaks the digestive functors model and can't be done. It would make a good feature request, though!

Resources