在Rails数据库中将两个独立的日期和时间字段连接或连接到一个日期时间条目似乎是一个相当常见的问题。在我的示例中,我试图将:dining_date与:dining_time连接起来,以生成:dining_date_and_time。但是,在谷歌上搜索并尝试了许多类似的stackoverflow解决方案后,我仍然不断收到我试图请求的soap xml Web服务返回的错误消息。
我在SoapUI和我正在使用的Web服务中玩过,我可以看到它对之前使用T非常敏感。这正是它所需的格式:
示例:2015-06-25T18:00:00
也许我在这里没有正确地插入"T"字符,但我尝试了很多组合,但都无法使其发挥作用。
总之,这里有来自Web服务的完整错误跟踪(我在Rails中使用Savon gem作为我的SOAP客户端,到目前为止它运行良好)
Savon::SOAPFault at /
(soap:Client) System.Web.Services.Protocols.SoapException: Server was unable to read request. ---> System.InvalidOperationException: There is an error in XML document (1, 607). ---> System.FormatException: The string '' is not a valid AllXsd value.
at System.Xml.Schema.XsdDateTime..ctor(String text, XsdDateTimeFlags kinds)
at System.Xml.XmlConvert.ToDateTime(String s, XmlDateTimeSerializationMode dateTimeOption)
at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReader1.Read32_BookReservationRequest(Boolean isNullable, Boolean checkType)
at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReader1.Read193_BookReservation()
at Microsoft.Xml.Serialization.GeneratedAssembly.ArrayOfObjectSerializer20.Deserialize(XmlSerializationReader reader)
at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle, XmlDeserializationEvents events)
--- End of inner exception stack trace ---
at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle, XmlDeserializationEvents events)
at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle)
at System.Web.Services.Protocols.SoapServerProtocol.ReadParameters()
--- End of inner exception stack trace ---
at System.Web.Services.Protocols.SoapServerProtocol.ReadParameters()
at
我观点的相关部分:
<h1>Book Reservation New Form:</h1>
<%= form_tag(root_url, :method => :get ) %>
<div class="field">
<%= label_tag 'Partner Code (identifies how user has reached this point - restaurant + channel):' %>
<%= text_field_tag :partner_code %>
</div>
<div class="field">
<%= label_tag 'Restaurant Location ID (which area in restaurant they are seating (dropdown):' %>
<%= text_field_tag :restaurant_location_id %>
</div>
<div class="field">
<%= label_tag 'Select dining session:' %>
<%= select_tag(:session_id, options_for_select([['Breakfast'], ['Lunch'], ['Dinner']])) %>
</div>
<div class="field">
<%= label_tag 'Dining Date:' %>
<%= text_field_tag(:dining_date) %>
</div>
<div class="field">
<%= label_tag 'Dining Date:' %>
<%= text_field_tag(:dining_time) %>
</div>
<div class="field">
<%= hidden_field_tag :dining_date_and_time %>
</div>
<div class="field">
<%= label_tag 'Party size: (dropdown)' %>
<%= select_tag(:size, options_for_select([['1 person', 1 ], ['2 people', 2], ['3 people', 3], ['4 people', 4], ['5 people', 5], ['6 people', 6], ['7 people', 7], ['8 people', 8], ['9 people', 9], ['10 people', 10], ['11 people', 11], [ '12 people', 12]])) %>
</div>
<%= render 'personal_details' %>
<%= render 'response' %>
我在模型中的最新尝试(也尝试了before_save和before_validation回调):
class BookReservation < ActiveRecord::Base
attr_reader :confirmation_number, :reservation_id
before_create do
self.dining_date_and_time = "#{dining_date} #{"T"} #{dining_time}"
end
def client
client = Savon.client(wsdl: "http://example-wsdl-link", follow_redirects: :follow_redirects)
end
def initialize(partner_code, restaurant_location_id, session_id, dining_date_and_time, size, first_name, last_name, email, guest_accepts_email_marketing)
message = { 'PartnerCode' => partner_code, 'RestaurantLocationId' => restaurant_location_id, 'SessionId' => session_id, 'DiningDateAndTime' => dining_date_and_time, 'Size' => size }
message.merge!('Booker' => {'UserWithoutALogin' => { 'FirstName' => first_name, 'LastName' => last_name, 'EMail' => email, 'GuestAcceptsEmailMarketingFromPartner' => guest_accepts_email_marketing }})
response = client.call(:book_reservation, message: message)
if response.success?
data = response.to_array(:book_reservation_response).first
if data
@confirmation_number = data[:confirmation_number]
@reservation_id = data[:reservation_id]
end
end
end
end
控制器:
class BookReservationsController < ApplicationController
def index
if params[:partner_code] && params[:restaurant_location_id] && params[:session_id] && params[:dining_date_and_time] && params[:size] && params[:first_name] && params[:last_name] && params[:email] && params[:guest_accepts_email_marketing]
@book_reservation = BookReservation.new params[:partner_code], params[:restaurant_location_id], params[:session_id], params[:dining_date_and_time], params[:size], params[:first_name], params[:last_name], params[:email], params[:guest_accepts_email_marketing]
end
end
def book_reservation_params
params.require(:book_reservation).permit(:partner_code, :restaurant_location_id, :session_id, :dining_date_and_time, :size, :first_name, :last_name, :email, :guest_accepts_email_marketing, :confirmation_number, :reservation_id, :dining_date, :dining_time)
end
end
模式中的表(dining_date_and_time是rails-datetime数据类型)(不需要仅用于基本的soap请求,但最终需要一个数据库来存储这些输入,如果没有它,BookReservation模型将无法作为Activerecord工作):
ActiveRecord::Schema.define(version: 20150625100835) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "book_reservations", force: :cascade do |t|
t.string "partner_code"
t.integer "restaurant_location_id"
t.string "session_id"
t.datetime "dining_date_and_time"
t.string "size"
t.string "first_name"
t.string "last_name"
t.string "email"
t.boolean "guest_accepts_email_marketing"
end
end
提前非常感谢!非常感谢您的帮助!
是的,您的插值不正确。引号中出现的内容都将出现在输出中,因此当您有"#{dining_date} #{"T"} #{dining_time}"
时,Rails将在"T"前后插入一个空白。
另外,我看不到您的日期和时间字段有任何格式或验证。用户输入日期"2015-06-25"和时间"18:00:00"的可能性很小。你可能想为你的日期实现一个日期选择器——用户可以从中选择一个很好的弹出日历,你可以使用它来强制执行你想要的格式。对于时间字段,如果不使用某种时间选择器控件,则需要添加一些验证,以确保用户输入正确的时间。这可以在您的模型中完成,也可以在客户端完成,以防止用户在无效时间内提交表单。
一旦输入有效,就可以根据需要使用strftime
进行格式化。从我的控制台:
>> foo = "2015-06-25" #from datepicker in your view
=> "2015-06-25"
>> bar = "2 pm" #validated time
=> "2 pm"
>> (foo + ' ' + bar).to_datetime.strftime("%Y-%m-%dT%H:%M:%S") #combine date and time and format as needed
=> "2015-06-25T14:00:00"
好吧,
首先,
此块:
before_create do
self.dining_date_and_time = "#{dining_date} #{"T"} #{dining_time}"
end
is not reached, before_create
fires when you save new object. Otherwise it would have thrown undefined local variable or method 'dining_date'
since your model has no chance to get this field.
Instead of checking in index
method of controller for persistence of required params, this should be validated in model like that
class BookReservation < ActiveRecord::Base
validate :your, :required, :fields, presence: :true
...
end
并且在您的控制器中应该只调用模型。
回到你最初的问题,史蒂夫做出了最好的决定。(foo + ' ' + bar).to_datetime.strftime("%Y-%m-%dT%H:%M:%S")
我冒着风险对你的代码进行了一点重构,并将其组织得更加"轨道式"
http://pastebin.com/CDhReHcA