Odoo error when generating One2many field data - python

Given the following class for the header:
class vontatas_head(models.Model):
_name = 'vontatas.head'
display_name = fields.Char(string="Sor", compute='_compute_display_name', store=False)
plan_type_id = fields.Many2one(
comodel_name='plan.type', string='Terv típus', required=True)
year = fields.Integer(string='Év', required=True, default=lambda *a: strftime('%Y'))
version = fields.Integer(string='Verzió', required=True, default=1)
comment = fields.Char(string='Megjegyzés')
vontatas_data_ids = fields.One2many(
comodel_name='vontatas.data', inverse_name='vontatas_id', string='Adatok', default=get_default_lines)
And for the detail:
class vontatas_data(models.Model):
_name = 'vontatas.data'
vontatas_id = fields.Many2one(comodel_name="vontatas.head", string="Vontatás sor")
name = fields.Char(string="Megnevezés", required=True)
code = fields.Char(string="Kód", required=True)
type = fields.Selection([('total', 'Összesen'), ('input', 'Input')], string="Típus", default='input')
value = fields.Float(string="Várható költség")
parent_id = fields.Many2one(comodel_name="vontatas.data", ondelete='cascade', string="Összesen sor")
child_ids = fields.One2many(comodel_name="vontatas.data", inverse_name='parent_id', string='Input sorok')
I have to automatically generate details data from a template with this code:
def get_default_lines(self):
self.env.cr.execute("select name, code, type, parent_id from vontatas order by code")
sorok = self.env.cr.fetchall()
ids = []
for sor in sorok:
ids.append((0,0, { 'name': sor[0],
'code': sor[1],
'type': sor[2],
'parent_id': sor[3]
}))
return ids
Everyting is working fine, but at the creation I have an error message: "One of the documents you are trying to access has been deleted, please try again after refreshing."
I know why this error is happening: at the moment of generation there is no valid parent_id.
My question is: how to provide any valid parent_id within the function get_default_lines, knowing that the data is actually just in memory, not stored yet in the database?
Or asked otherwise: How to keep the hierarchy level defined within the template?

Related

How can I create a ManyToMany relationship with the same model in Django and save to database?

I have a form which is collecting data about a variable to be created. I want to create a list of variables which are already there in the database. I am doing this by creating a ManyToMany relationship. When I start the server, the list of variables gets saved on the application, but it does not alter the database field named selective list.
forms.py
class VariableForm(ModelForm):
class Meta:
model = variable
fields = ['name', 'area', 'parameterName', 'order', 'type', 'format', 'units', 'comboItems',
'hiAlarm', 'loAlarm', 'scaleHiMax', 'scaleLoMax', 'deviationAlarm','selectiveList', 'round',
'days', 'required', 'hidden', 'readOnly', 'holdLast', 'calibrationFrequency',
'dateNextCalibration', 'triggerCalibrated']
widgets = {
'comboItems': forms.Textarea(attrs={'rows':1, 'cols': 40, 'style': 'height: 2em;padding-top:0'}),
'forceValue': forms.Textarea(attrs={'rows':1, 'cols': 40, 'style': 'height: 2em;padding-top:0',
'placeholder':'This will force all input to this variable'})
}
#selectiveList = forms.ModelMultipleChoiceField(queryset=variable.objects.all().order_by('-name').reverse())
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(VariableForm, self).__init__(*args, **kwargs)
self.fields['round'] = forms.ModelMultipleChoiceField(
queryset=opRound.objects.all(),
widget=forms.SelectMultiple,
label='Rounds',
required=False
)
self.fields['selectiveList'] = forms.ModelMultipleChoiceField(
queryset=variable.objects.all().order_by('-name').reverse(),
widget=forms.SelectMultiple,
label='Select Variables',
required=False
)
self.fields['days'] = forms.ModelMultipleChoiceField(
queryset=dayOfWeek.objects.all(),
widget=forms.SelectMultiple,
label='Days',
required=False
)
self.fields['area'].choices = AreaIterator(request = self.request)
try:
self.fields['order'].initial = variable.objects.latest().order+1
except Exception,e:
print e
model.py
class variable(models.Model):
name = models.CharField(max_length=255)
area = models.ForeignKey(area)#parent
order = models.IntegerField("Order Index (0 is first, 1 is next, etc.)",default=999)#order index to display in order correctly, ascending
type = models.CharField(max_length=255, choices=(("Value", "Value"), ("Runtime", "Runtime/FlowTotal"),
("Message", "Message"), ("CheckBox", "Check Box List"), ("Selection", "Selection Box"), ("Formula2", "Formula with other Variables"),
("OnOff", "In/Out of Service Selection"), ("OnOffSelection", "Selective On/Off")),
default = "Value" )#what type of variable
format = models.CharField(max_length=255,choices=(("Number", "Number (Without Decimals)"),
("2Number", "Number (With Decimals)"), ("Date", "Date"),
("Time", "Time"), ("Text", "Text")), blank=True, null=True, default="2Number" )#number format if needed
units = models.CharField(max_length=255,blank=True,null=True)#units of measurement
required = models.BooleanField(default=True)#is the variable required in a round
hiAlarm = models.FloatField("High Alarm",blank=True,null=True)#red notify if above
loAlarm = models.FloatField("Low Alarm",blank=True,null=True)#yellow notify if below
scaleHiMax = models.FloatField("Limit maximum value",blank=True,null=True)#scale to high max if needed
scaleLoMax = models.FloatField("Limit low value",blank=True,null=True)#scale to low if needed
deviationAlarm = models.FloatField("Deviation Alarm",blank=True,null=True,
help_text="Triggers an alarm if the value between this record and the last is greater than this percentage.")#%change check
round = models.ManyToManyField(opRound,blank=True)#round of gathering data
days = models.ManyToManyField(dayOfWeek,blank=True)#day of the week
selectiveList = models.ManyToManyField("self",through="variable",blank=True,symmetrical=False)#List to determine which variables to display when the selection is "OFF"
parameterName = models.CharField("Parameter Name (ReportID)",max_length=255,blank=True,null=True)#hachWIM ID
comboItems = models.TextField("List of comma separated options.", blank=True,null=True)#list deliminated by a column for choosing
#PUT EQUATION HERE
hidden = models.BooleanField("Sync to tablet",default=True)#this muse be True if required is true
readOnly = models.BooleanField("Read only.", default = False)
dateTimeEdited = models.DateTimeField(auto_now=True)#date edited
userEdited = models.ForeignKey(CustomUser,blank=True,null=True,related_name="userEditedV")# last user to edit data
forceValue = models.TextField(blank=True,null=True)#force reading
userForced = models.ForeignKey(CustomUser,blank=True,null=True,related_name="userForcedV")# last user to force data
useForce = models.BooleanField("Turn Forcing On", default=False)
dateNextCalibration = models.DateField("Next Calibration Date", blank=True,null=True, help_text="YYYY-MM-DD")
triggerCalibrated = models.BooleanField("Trigger next calibration date", default=False)
calibrationFrequency = models.ForeignKey(calibrationFrequency, blank=True, null=True)
version = models.BigIntegerField(default=1)#used for sync
holdLast = models.BooleanField("Selecting this will hold the last value on the tablet automatically.", default=False)
When we are trying to do it from different models, such as round or days, it creates a new database table with those relations. I want to store the selected values as a string list in the selective list column in the same model.
Here is what the multiple select looks like.

api.constrain for give condition if one of field is empty

I tried to use api.constrain in odoo. The case is, I want to give condition to odoo if user missing one field, odoo will give some warning like alert, and user have to give some input to the previous field. This is my code :
class Provinc(models.Model):
_name = 'provinsi.model'
_rec_name ='prov'
prov = fields.Char(
string='Provinsi',
)
res_partner_ids = fields.One2many(
'res.partner',
'provinsi'
city_id = fields.One2many(
'city.model',
'provinsi_id'
)
class city(models.Model):
_name = 'kota.model'
_rec_name ='city'
city = fields.Char(
string='City',
)
res_partner_city = fields.One2many(
'res.partner',
'city'
)
provinsi_id = fields.Many2one(
'provinsi.model',
string='provinsi',
)
class Master_data(models.Model):
_inherit = 'res.partner'
provinsi = fields.Many2one(
'provinsi.model',
'provinsi',
)
city = fields.Many2one(
'city.model',
'city',
)
#api.onchange('provinsi')
def show_provinsi(self):
return {'domain': {'city': [('provinsi_id', '=', self.provinsi.id)]}}
#api.constrains('provinsi')
#api.onchange('city')
def show_kota(self):
for record in self:
if record.provinsi == False:
raise ValidationError("is gonna be error")
I have tried 2 ways. First, I put the api.constrain insode class City, and it's doesn't work,second ways I tried put the api.constrain like the code inside class master, and the result remains the same.
you only need onchang not any constrains to display warning!
#api.onchange('city')
def show_kota(self):
if self.city and not self.provinsi:
raise ValidationError("is gonna be error")
now if you select value in city field and provinsi missing value then it will display error msg
guessing field "city" and "provinsi" is in 'res.partner' form view!
If you want to check a field value is empty or not, just use required ="1" like:
<field name="provinsi" required="1"/>

Cassandra's Python CQLEngine model inheritance

I have the following object model:
class Data(Model):
__keyspace__ = 'varilog'
__table_name__ = 'md_data'
id = columns.TimeUUID(partition_key=True, primary_key=True, required=True)
device = columns.Text(primary_key=True, required=True)
property = columns.Text(primary_key=True, required=True)
field = columns.Text(primary_key=True, required=True)
cyclestamp = columns.DateTime(static=True)
type = columns.Text(discriminator_column=True)
#text_value = columns.Text() # Will work
#value = columns.Text(db_field='text_value') # Will work but...
class DataText(Data):
__discriminator_value__ = 'str'
value = columns.Text(db_field='text_value') # Always None
#text_value = columns.Text() # Ok also
When I query an object, depending on the value of the type column, the correct object is returned (DataText in this example), however it's value is None while if I uncomment text_value I'll have the correct value.
It looks like db_field is not supported in a child class. Is this a bug?

django - can't assign a foreign key

For unknown reasons, I cannot assign a foreign key instance of Item_rarity table into Detailed_item table. Django throws an error:
Cannot assign "u'Basic'": "Detailed_item.rarity" must be a "Item_rarity" instance.
... But in Item_rarity dictionary "Basic" record exists - I can choose it from admin panel and create Detailed_item record manually.
I have defined models:
class Detailed_item(models.Model):
item_id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=50)
level = models.IntegerField()
icon = models.CharField(max_length=150)
rarity = models.ForeignKey('Item_rarity')
general_type = models.ForeignKey('Item_type')
detailed_type = models.ForeignKey('Item_detailed_type')
class Item_rarity(models.Model):
name = models.CharField(max_length=15, primary_key=True)
class Item_type(models.Model):
name = models.CharField(max_length=15, primary_key=True)
class Item_detailed_type(models.Model):
name = models.CharField(max_length=20, primary_key=True)
In views, I try to populate it in this manner (inserting multiple items):
...
items = get_all_items() #get dict of items
for element in items:
tmp_det_type = ''
for key, val in element.iteritems():
#get 'detailed type' from inner dict
if key == "type":
tmp_det_type = val
item = Detailed_item(
item_id=element['id'],
name=element['name'],
level=element['level'],
icon=element['icon'],
rarity=element['rarity'], #error
general_type=element['type'],
detailed_type=tmp_det_type,
)
item.save()
...
I even tried to hard code "Basic" string, but it doesn't work either.
* Solved *
Next two entries, that is Item_type and Item_detailed_type were also invalid.
Correct code:
from app.models import Detailed_item, Item_rarity, Item_type, Item_detailed_type
...
items = get_all_items() #get dict of items
for element in items:
tmp_det_type = ''
for key, val in element.iteritems():
#get 'detailed type' from inner dict
if key == "type":
tmp_det_type = val
#create objects with string values
obj_rarity = Item_rarity(name=element['rarity'])
obj_item_type = Item_type(name=element['type'])
obj_item_detailed_type = Item_detailed_type(name=tmp_det_type)
item = Detailed_item(
item_id=element['id'],
name=element['name'],
level=element['level'],
icon=element['icon'],
rarity=obj_rarity,
general_type=obj_item_type,
detailed_type=obj_item_detailed_type,
)
item.save()
...
Item_rarity instance should be passed while storing Detailed_item object since Item_rarity is a foreign key related object in Detailed_item.
Its that you might have passed the Basic string instead of the <Basic Object> itself.
While creating an object in django using its ORM, any foreign_key related object should be provided with the instance itself instead of the id(pk) of the object, where as while fetching the data from the database you can use either of instance or the id(pk) of the instance.
class ParentModel(models.Model):
model_field = models.CharField(max_length=16)
class MyModel(models.Model):
some_field = models.ForeignKey('ParentModel')
parent_model = ParentModel.objects.create(model_field='some_data')
my_model = MyModel.objects.create(some_field=parent_model)
^^^^^^^^^^^^
Note here that the parent_model object itself is passed instead of the id
While fetching the data back,
parent_model = ParentModel.objects.get(model_field='some_data')
my_model = MyModel.objects.get(some_field=parent_model)
or
my_model = MyModel.objects.get(some_field=parent_model.id)
Both would work in case of data fetch.
You do not have to provide the related object on creation if you change the kwarg in to rarity_name:
item = Detailed_item(
item_id=element['id'],
name=element['name'],
level=element['level'],
icon=element['icon'],
rarity_name=element['rarity'], # no error
general_type=element['type'],
detailed_type=tmp_det_type,
)
I have only tested this with the regular id field (the auto pk) but it
should work with your primary key just fine.
E.g.
class SimpleModel(Model):
value = TextField(blank=True)
class ComplexModel(Model):
simple = ForeingKey(SimpleModel)
title = TextField(unique=True)
ComplexModel.objects.create(title='test', simple_id=1)

Can not check form values with a ChoiceField

I am working on a catalog of classified grouped by categories.
However when I submit my form, I get the following error message:
Caught ValueError while rendering: Cannot assign "u'9'": "Classified.category" must be a "Category" instance.
I believe this is because Django expects a Category objects instead of a simple integer which corresponds to the chosen Category ID.
Here is how I wrote the system:
A classified is linked to one category.
The category system is hierarchical with a parent category and a list of child categories.
For example I have something like this:
Electronics
|-- IPad
|-- IPods
|-- ...
So I have the following models:
class Category(BaseModel):
# [...]
name = models.CharField(u'Name', max_length=50)
slug = AutoSlugField(populate_from='name', slugify=slugify, unique=True,
unique_with='name', max_length=255, default='')
parent = models.IntegerField(u'parent', max_length=10, null=False,
default=0)
objects = CategoryManager()
# [...]
class Classified(BaseModel):
# [...]
category = models.ForeignKey(Category, related_name='classifieds')
I created the following manager:
class CategoryManager(Manager):
def categoryTree(self):
tree = self.raw("SELECT"
" P.id, P.name parent_name, P.slug parent_slug, P.id parent_id,"
" C.name child_name, C.slug child_slug, C.id child_id"
" FROM classified_category C"
" LEFT JOIN classified_category P ON P.id = C.parent"
" WHERE C.parent <> 0"
" ORDER BY P.name, C.name;")
categoryTree = []
current_parent_id = tree[0].parent_id
current_parent_name = tree[0].parent_name
option_list = []
for c in tree:
if current_parent_id != c.parent_id:
categoryTree.append((current_parent_name, option_list))
option_list = []
current_parent_id = c.parent_id
current_parent_name = c.parent_name
option_list.append((c.child_id, c.child_name))
categoryTree.append((current_parent_name, option_list))
return category
And my Django form contains the following:
class ClassifiedForm(forms.ModelForm):
# [...]
category = forms.ChoiceField(label=u'Category', required=True,
choices=Category.objects.categoryTree(), widget=forms.Select())
# [...]
If I use category = forms.ModelChoiceField(Category.objects.all()) everything works fine but I need to control how the <select> field is displayed with a list of <optgroup>. This is why use categoryTree()
But unfortunately using CategoryManager.categoryTree() breaks my form validation and I do not know how to fix my problem.
If I could be pointed to where I was wrong and how I can fix this, that would be awesome.
Thanks in advance for your help.
Quick solution is to save category manually
class ClassifiedForm(forms.ModelForm):
# [...]
category = forms.ChoiceField(label=u'Category', required=True,
choices=Category.objects.categoryTree(), widget=forms.Select())
class Meta:
exclude=('category',)
def save(self):
classified = super(ClassifiedForm, self).save(commit=False)
classified.category = Category.objects.get(id=self.cleaned_data['category'])
classified.save()
return classified
You can and should still use a ModelChoiceField. The list of choices can be modified in the init method of the form class - i.e.
class ClassifiedForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(ClassifiedForm, self).__init__(*args, **kwargs)
# Set the queryset for validation purposes.
# May not be necessary if categoryTree contains all categories
self.fields['category'].queryset = Category.objects.categoryTreeObjects()
# Set the choices
self.fields['category'].choices = Category.objects.categoryTree()
Also, you should look carefully at the django-mptt package. It looks like you may be reinventing the wheel here.

Categories

Resources