Вставить временную метку по умолчанию в FromJSON

#haskell #yesod #aeson

#haskell #yesod #aeson

Вопрос:

У меня есть вызов ajax, отправляющий json на маршрут в Yesod, и я хочу, чтобы маршрут проанализировал json и вставил его непосредственно в базу данных. В моем файле модели у меня есть

 createtime UTCTime default=now()
  

что предотвращает синтаксический анализ json, поскольку клиент не отправляет время создания. Я пытался написать свой собственный parseJSON для записей журнала, но мне не удалось вставить значение UTCTime по умолчанию, поскольку getCurrentTime возвращает значение в монаде ввода-вывода. Я бы хотел, чтобы база данных установила значение, если это возможно.

Единственное, что я могу придумать на данный момент, это создать тип, подобный LogEntryWithoutTime, проанализировать JSON в нем и преобразовать в LogEntry. Есть ли более простой способ?

Редактировать: я показываю три разных сбоя при добавлении getCurrentTime в синтаксический анализ JSON. Во-первых, цель состоит в том, чтобы проанализировать createtime, если доступно, и по умолчанию getCurrentTime на сервере. В любом случае это неправильно, поскольку мы не должны полагаться на время клиента.

 instance FromJSON Log where
    parseJSON (Object o) = Log
        <$> o .: "userid"
        ...
        <*> o .:? "createtime" .!= liftIO getCurrentTime
  

Ошибка

 Model.hs:58:32:
Couldn't match expected typeUTCTime
            with actual type ‘m0 UTCTime
In the second argument of ‘(.!=)’, namely ‘liftIO getCurrentTime’
In the second argument of ‘(<*>)’, namely
  ‘o .:? "createtime" .!= liftIO getCurrentTime’
  

Во-вторых, я пытаюсь просто получить текущее время.

 <*> liftIO getCurrentTime
  

и я получаю сообщение об ошибке

 Model.hs:58:9:
No instance for (MonadIO
                   aeson-0.7.0.6:Data.Aeson.Types.Internal.Parser)
  arising from a use of ‘liftIO’
In the second argument of ‘(<*>)’, namely ‘liftIO getCurrentTime’
  

Если я изменю строку на

 <*> getCurrentTime
  

затем я получаю

 Model.hs:58:9:
Couldn't match typeIO
              with ‘aeson-0.7.0.6:Data.Aeson.Types.Internal.ParserExpected type: aeson-0.7.0.6:Data.Aeson.Types.Internal.Parser
                 UTCTime
  Actual type: IO UTCTime
  

Комментарии:

1. Вы разобрались с этой проблемой? Сейчас я борюсь с этим.

2. Извините, я не смог найти хорошее решение.

Ответ №1:

Проблема с default атрибутом, определенным в модели, заключается в том, что вам все равно нужно определить все значения при сохранении объектов. Значения по умолчанию существуют только для автоматической миграции и определений схемы, насколько мне известно.

Попробуйте удалить getCurrentTime из монады ввода-вывода (с помощью liftIO ).

Комментарии:

1. Я вижу, что файл модели не влияет на код Haskell, поэтому я закончил тем, что написал raw SQL для вставки строки. Я пытался использовать liftIO, когда я опубликовал этот вопрос, и это не сработало, но я не помню точную ошибку. Я думаю, что он пытался поместить ее в другую монаду, но другой монады не было.

2. Нет экземпляра для (MonadIO aeson-0.8.0.2:Данные. Aeson. Типы. Internal.Parser)

Ответ №2:

Сейчас я сталкиваюсь по существу с той же проблемой. В настоящее время я думаю, что, поскольку я не ожидаю, что пользователь предоставит мне полную запись, я не должен моделировать ее таким образом. Вместо этого я должен смоделировать тело запроса, которое я ожидаю от пользователя, и преобразовать его самостоятельно для хранения.

Итак, у вас может быть концепция PartialLog , в которой есть только поля, которые вы ожидаете отправить пользователю:

 data PartialLog = PartialLog { partialLogMessage :: Text }
  

Тогда у вас может быть функция, которая заполняет пробелы и предоставляет вам полную запись:

 {-# LANGUAGE RecordWildCards #-}

logFromPartial :: PartialLog -> UserId -> IO Log
logFromPartial p u = do
  let logUserId  = u
      logMessage = partialLogMessage p
  logCreatetime <- liftIO getCurrentTime
  return Log{..}
  

примечание.б. Я передаю UserId , потому что мы можем захотеть, чтобы это значение поступало из сеанса аутентификации; пользователю не нужно сообщать нам, какой у него идентификатор.