Created Expected created to be (Time | Nil) but got (Float64 | String) - oop

I am new to this amber framework,crystal lang and object oriented programming in general . I was following the tutorials and tried to create a simple form using this scaffold
amber g scaffold item name:string path:string type:string size:float created:date
I can see that this class has been created on the models folder
class Item < Granite::Base
connection pg
table items
column id : Int64, primary: true
column name : String?
column path : String?
column type : String?
column size : Float64?
column created : Time?
timestamps
end
When i launched the app and tired to insert a new item i got this error
Created Expected created to be (Time | Nil) but got (Float64 |
String).
This is the code of the .slang form
== form(action: "/items/#{item.id.to_s}", method: item.id ? :patch : :post) do
== csrf_tag
.form-group
== text_field(name: "name", value: item.name, placeholder: "Name", class: "form-control")
.form-group
== text_field(name: "path", value: item.path, placeholder: "Path", class: "form-control")
.form-group
== text_field(name: "type", value: item.type, placeholder: "Type", class: "form-control")
.form-group
== text_field(name: "size", value: item.size, placeholder: "Size", class: "form-control")
.form-group
== text_field(name: "created", value: item.created, placeholder: "Created", class: "form-control")
== submit("Submit", class: "btn btn-success btn-sm")
== link_to("Back", "/items", class: "btn btn-light btn-sm")
I am guessing that when i input a value like 2020-01-01 00:01:00 this is handled as a string but i need this to be converted to a Time type. I presume this needs to happen on the related controller file, but i don't know how to do this.
This is the code that gets executed when i try to create a new item.
def create
item = Item.new item_params.validate!
if item.save
redirect_to action: :index, flash: {"success" => "Item has been created."}
else
flash[:danger] = "Could not create Item!"
render "new.slang"
end
end
thanks,
gurrurin

I was able to make thsi work by creating some new fucntions
class Item < Granite::Base
...
def set_id (id : Int64)
#id = id
end
def set_name (name : String)
#name = name
end
def set_filetype (filetype : String)
#filetype = filetype
end
def set_filesize (filesize : Float64)
#filesize = filesize
end
def set_filepath (filepath : String)
#filepath = filepath
end
def set_created (cr : Time)
#created = cr
end
and then used those after inilializing an empty item
item = Item.new
item.set_name(params[:name])
item.set_filetype(params[:filetype])
item.set_filesize(params[:filesize].to_f64)
item.set_filepath(params[:filepath])
item.set_created(Time.parse(params[:created],"%Y-%m-%d %H:%M:%S %z", Time::Location::UTC))
though i dont think this is the best solution here .
Since this parmas object stores everything as strings i suspect that when you initialize a new item with this the a string like ' 2020-01-01 00:01:00 UTC' is not converter to the apropriate Time type. Other types like Int or Float seem to work fine with no isseus.
item = Item.new item_params.validate!

You don't need any of those extra setter methods, they are already defined by the column macro. You don't need an extra created column, there are already created_at and updated_at columns created by the timestamps macro and they are updated for you when creating a new record and saving a modified record.
Other than that if you DID create a column that was a Time type you would do
item.created = Time.parse(params[:created],"%Y-%m-%d %H:%M:%S %z", Time::Location::UTC)
and Granite will do the right conversion for each database engine when you save and read the record.

Related

DDL source Z1127_EXTVIEW of type ABAP Dictionary Type cannot be converted to Extend (-> long text)

I have the table Z1127_STUDENT:
define table z1127_student {
key id : z1127_sample not null;
branch : z1127_sample1;
age : z1127_age;
address : z1127_address;
percentage : z1127_percent;
}
(the types of the columns are based on the types int2 and char)
and the CDS view Z1127_CDS_VIEWS:
#AbapCatalog.sqlViewName: 'Z1127_CDSVIEWS'
#AbapCatalog.compiler.compareFilter: true
#AbapCatalog.preserveKey: true
#AccessControl.authorizationCheck: #CHECK
#EndUserText.label: 'CDS VIEWS'
define view Z1127_CDS_VIEWS as select from z1127_student
{
key id,
branch,
age
}
I tried to create this extended view :
#AbapCatalog.sqlViewAppendName: 'Z1127_SQL_3'
#EndUserText.label: 'cds view 3'
extend view Z1127_CDS_VIEWS with z1127_cds3 {
address,
percentage
}
But it's showing this error :
DDL source z1127_cds3 of type ABAP Dictionary Type cannot be converted to Extend (-> long text)
How to avoid this error?

Concatenate fields on Odoo v9

I'm trying to concatenate 3 fields to form a internal code and display it in the views:
I have 3 models:
Category (size=2)
Product (size=4)
Serie (size=3)
And I want to display it in the form like this
Product Code: CAT-PROD-001
I don't know if i have to use a computed field or if exist anoter way to do this, because I was doing test with computed fields but can't reach the desired output.
Edit:
Now I'm trying to use a computed field with a onchange function to generate the value on the field
MODEL
# -*- coding:utf-8 -*-
from openerp import models,fields,api
class exec_modl(models.Model):
_name = "exec.modl"
_rec_name = "exec_desc"
exec_code = fields.Char('Identificador',required=True,size=3)
exec_desc = fields.Char('DescripciĆ³n',required=True)
cour_exec = fields.Many2one('cour.modl')
proc_exec = fields.Many2one('enro.modl')
inte_code = fields.Char(compute='_onchange_proc')
FUNCTION
#api.onchange('proc_exec')
def _onchange_proc(self):
cate = "XX"
cour = "XXXX"
exet = "XXX"
output = cate+"-"+cour+"-"+exet
return output
I'm just trying with plain values just to know how to send it to the field.
EDIT 2:
Using the answer from #Charif I can print the static strings on the form, but the next milestome I'm trying to reach is getting the codes (external models fields) to crate that inte_code
ex: From the model cour.modl I want to get the value from the field cour_code(internal_id for course) corresponding to the cour_exec field on the first model (the cour_exec field have the description of the course from cour.modl model)
#api.depends('proc_exec')
def _onchange_proc(self):
cate = "XX"
cour = self.env['cour.modl'].search([['cour_desc','=',self.cour_exec]])
exet = "XXX"
output = cate+"-"+cour+"-"+exet
self.inte_code = output
E #api.depends('inte_code')
def _onchange_proc(self):
cate = "XX"
# first domain use tuple not list
cour_result = self.env['cour.modl'].search([('id','=',exec_modl.cour_exec)]).cour_code
cour = "" # empty string because you cannot contcatenate None or False with a string value
#if cour_result :
# cour = ",".join(crse_code for crse_code in cour_result.ids)
#else :
# print "result of search is empty check you domain"
exet = "XXX"
output = cate+"-"+cour+"-"+exet+"-"+cour_result
self.inte_code = output
EDIT 3
I've been trying to usse the search mode calling other model values but I have the console output :
Can't adapt type 'Many2One' , seems im trying to compare 2 different type of fields, the types can be parsed on odoo ? or I'm using a wrong syntax for search method?
#api.depends('inte_code')
def _onchange_proc(self):
cate = "XX"
# first domain use tuple not list
cour_result = self.env['cour.modl'].search([('id','=',exec_modl.cour_exec)]).cour_code
exet = "XXX"
output = cate+"-"+cour+"-"+exet+"-"+cour_result
self.inte_code = output
EDIT 4 : ANSWER
Finally I've reach the desired output! using the following code:
#api.depends('inte_code')
def _onchange_proc(self):
cate_result = self.cate_exec
proc_result = self.env['enro.modl'].search([('id','=',str(self.proc_exec.id))]).enro_code
cour_result = self.env['cour.modl'].search([('id','=',str(self.cour_exec.id))]).cour_code
output = str(proc_result)+"-"+str(cate_result)+"-"+str(cour_result)+"-"+self.exec_code
self.inte_code = output
Additionaly I've added a related field for add the course category to the final output.
cate_exec = fields.Char(related='cour_exec.cour_cate.cate_code')
Now the output have this structure:
INTERNAL_PROC_ID-CAT_COURSE-COURSE-EXECUTION_CODE
EX: xxxxxxxx-xx-xxxx-xxx
First in compute field use api.depends not onchange :
Second the compute function don't return anything but it passes the record on the self variable so all you have to do is assign the value to the computed field.
#api.depends('proc_exec')
def _onchange_proc(self):
# compute the value
# ...
# Than assign it to the field
self.computed_field = computed_value
one of the thing that i recommand to do is to loop the self because it's recordSet so if the self contains more than one record this previous code will raise signlton error
so you can do this :
# compute the value here if it's the same for every record in self
for rec in self :
# compute the value here it depends on the value of the record
rec.compute_field = computeValue
or use api.one with api.depends
#api.one
#api.depends('field1', 'field2', ...)
EDITS:
#api.depends('proc_exec')
def _onchange_proc(self):
cate = "XX"
# first domain use tuple not list
cour_result = self.env['cour.modl'].search([('cour_desc','=',self.cour_exec)])
cour = "" # empty string because you cannot contcatenate None or False with a string value
if cour_result :
cour = ",".join(id for id in cour_result.ids)
else :
print "result of search is empty check you domain"
exet = "XXX"
output = cate+"-"+cour+"-"+exet
self.inte_code = output
try this code i think the result of search is a recordSet so you can get the list of ids by name_of_record_set.ids than create a string from the list of ids to concatenate it try and let me know if there is an error because i'm using work PC i don't have odoo on my hand ^^
You can create new wizard.
From wizard you can generate Internal Reference.
class create_internal_reference(models.TransientModel):
_name="create.internal.reference"
#api.multi
def create_internal_reference(self):
product_obj=self.env['product.product']
active_ids=self._context.get('active_ids')
if active_ids:
products=product_obj.browse(active_ids)
products.generate_new_internal_reference()
return True
Create View & act_window
<record model="ir.ui.view" id="create_internal_reference_1">
<field name="name">Create Internal Reference</field>
<field name="model">create.internal.reference</field>
<field name="arch" type="xml">
<form string="Create Internal Reference">
<footer>
<button name="create_internal_reference" string="Generate Internal Reference" type="object" class="oe_highlight"/>
<button string="Cancel" class="oe_link" special="cancel" />
</footer>
</form>
</field>
</record>
<act_window name="Generate Internal Reference" res_model="create.internal.reference"
src_model="product.product" view_mode="form" view_type="form"
target="new" multi="True" key2="client_action_multi"
id="action_create_internal_reference"
view_id="create_internal_reference_1"/>
class product_product(models.Model):
_inherit='product.product'
#api.multi
def generate_new_internal_reference(self):
for product in self:
if not product.internal_reference:
product.internal_reference='%s-%s-%s'%(str(product.categ_id.name)[:2],str(product.name)[:4],third_field[:3])
From product.product under more button you can access this wizard and generate internal reference.
This may help you.

Custom data in one2many field in Odoo

I am working on this Odoo assignment. I have to make a custom module in which the requirement is like this.
There is a form say "Notebook" it contains a field that comes from 'hr.employee' i.e. Many2one. Next thing this form will contain is a table which 3 columns (Quality, Score, Comment). Now Qualities must be a master table which contains many qualities name.
I have achieved this task in the way SalesOrder form works i.e. for a particular sales order there are multiple sales lines.
But I want all the qualities to be there on form with default score value of 0.
Here is the code
Please tell me the resolution
class qualities_fields(models.Model):
_name = "ayda.qualities.fields"
_description = "Contains only Attributes"
#api.multi
def name_get(self):
data = []
for rows in self:
value = ''
value += rows.quality_name
data.append((rows.id, value))
return data
quality_name = fields.Char(string="Quality Name")
class qualities_data(models.Model):
_name = "qualities.data"
_description = "All points mandatory to be filled"
quality_id = fields.Many2one('notebook', string="Quality IDs")
quality_name = fields.Many2one('qualities.fields', string="Quality Name")
score = fields.Integer(string="Score")
comment = fields.Char(string="Comment")
class notebook(models.Model):
_name = "notebook"
_description = "Checking one2many of qualities"
def createRecords(self):
cr = self.pool.cursor()
quantity_fields = self.pool.get('qualities.fields').search(cr, self.env.uid, [])
quantity_lines = []
for field in quantity_fields:
quality_data = {
'quality_id' : self.id,
'quality_name' : field.id,
'score' : 0,
'comment' : ''
}
quantity_lines.append(quality_data)
return quantity_lines
name = fields.Many2one('hr.employee', string="Name")
qualities_line = fields.One2many('qualities.data', 'quality_id', string="Qualities Line", default=createRecords)
There's an easier way to do this, on the field definition just set the default score to 0
score = fields.Integer(string="Score", default=0)
That way all scores would be zero when they're created

Is there a way to have shorter `_cls` values in mongoengine?

Is there a way to have shorter _cls values in mongoengine, apart from making the names of the classes shorter (which would make code difficult to read)?
I was looking for something like this:
class User(Document):
login = StringField(primary_key = True)
full_name = StringField()
meta = { "short_class_name": "u" }
class StackOverFlowUser(User):
rep = IntField()
meta = { "short_class_name": "s" }
If the short_class_name meta attribute existed (but I have not found it or anything similar), then we could have this:
{ "_cls" : "s", "_id" : "john",
"full_name" : "John Smith", "rep" : 600 }
instead of this:
{ "_cls" : "User.StackOverFlowUser", "_id" : "john",
"full_name" : "John Smith", "rep" : 600 }
In this example, this leads to about 20% space saving, and in some cases, it could be even greater.
I guess mongoengine is open source, I could go ahead and code this, but if you know a simpler solution, I would love to hear it.
Thanks.
After looked into mongoengine's source code I and in most part MiniQuark got next hack:
def hack_document_cls_name(cls, new_name):
cls._class_name = new_name
from mongoengine.base import _document_registry
_document_registry[new_name] = cls
or as class decorator:
def hack_document_cls_name(new_name):
def wrapper(cls):
cls._class_name = new_name
from mongoengine.base import _document_registry
_document_registry[new_name] = cls
return cls
return wrapper
We see no other way than hacking with the _class_name and the _document_registry.
When you want to rename a class, you must apply this hack immediately after the class definition (or at least before you define any sub-classes, or else they will have a _types attribute with the base class's long name). For example:
class User(Document):
login = StringField(primary_key = True)
full_name = StringField()
hack_document_cls_name(User, "u")
class StackOverflowUser(User):
rep = IntField()
hack_document_cls_name(StackOverflowUser, "s")
or as class decorator:
#hack_document_cls_name("u")
class User(Document):
login = StringField(primary_key = True)
full_name = StringField()
#hack_document_cls_name("s")
class StackOverflowUser(User):
rep = IntField()
Ok, so far, the best I could come up with is this. It works, but I'm sure there must be less hackish solutions...
class U(Document): # User
meta = { "collection": "user" }
login = StringField(primary_key = True)
full_name = StringField()
class S(U): # StackOverflowUser
rep = IntField()
User = U; del(U)
StackOverflowUser = S; del(S)
User.__name__ = "User"
StackOverflowUser.__name__ = "StackOverflowUser"
Now when I do this:
StackOverflowUser.objects.create(login="john", full_name="John Smith", rep=600)
I get this document in the user collection:
{ "_cls" : "U.S", "_id" : "john", "full_name" : "John Smith", "rep" : 600 }
Compared to the standard behavior, this is about 20% space saving. But I don't like how hackish it is.

Remove default 0 from numeric textbox

My model has an EditorFor that binds to a not null numeric field in a database. I wish to keep this field blank so that users can enter or scan numbers into the field. Unfortunately, it defaults to a 0. Is there an easy way to remove the 0 while keeping the field not null?
#Html.EditorFor(model => model.RackNumber, new { id = "RackNumber"})
Change model property type to nullable: public int? RackNumber {get;set;}
You can provide the Value attribute like this:
#Html.EditorFor(model => model.RackNumber, new { Value = Model.RackNumber == 0 ? "" : Model.RackNumber.ToString(), id = "RackNumber"})