I have a class student_student which have a one2many fieldresult_ids defined like the following:
result_ids = fields.One2many("schoolresults.detail", "student_id", "School Results", default="_get_subjects")
and
def _get_subjects(self):
cr = self.pool.cursor()
self.env
return self.pool.get('schoolresults.subject').search(cr, self.env.uid, [])
in the other side I have a class schoolresults_subject:
class schoolresults_subject(models.Model):
_name = "schoolresults.subject"
_description = "Student's subjects."
name = fields.Char("Subject")
class schoolresults_detail(models.Model):
_name = "schoolresults.detail"
_description = "Student's results."
student_id = fields.Many2one("student.student", "Student", ondelete="cascade")
subject_id = fields.Many2one("schoolresults.subject", "Subject")
result = fields.Float("Result", compute='_compute_value', store=True)
What I'm trying to do is to fill the result_ids with a subjects list from the last class, whenever the user trying to create a new student profile, using the the default parameter in the one2many field.
But whenever I try to create a student profile I get this error Wrong values for student.student.result_ids.
Please is there anyway to achieve that?
PS. I'm using Odoo 9
I don't get your requirements here fully, but try something like the following:
def _get_subjects(self):
subjects = self.env['schoolresults.subject'].search([])
details = self.env['schoolresults.detail']
for subject in subjects:
details |= details.new({'subject_id': subject.id})
return details
But explaining the error message: you're returning a RecordSet of schoolresults.subject but your field result_ids has the comodel schoolresults.detail. That's just wrong ;-)
I could do this by overriding the default_get method:
def default_get(self, fields):
res = super(student_student, self).default_get(fields)
srd = self.env['schoolresults.detail']
ids=[]
school_result={'subject_id':1,'result':0} #dict for fields and their values
sr = srd.create(school_result)
ids.append(sr.id)
res['result_ids'] = ids
return res
This is how to override default_get for one2many field.
Credit goes to:Default values for one2many
Please read this If you want to set the default values of one2many field from action, you can create a list of tuple with the same format we use for create or write one2many field and then you can pass it to context.
You can copy your context-
context = self.env.context.copy()
Prepare one2many values like this--
pr_lines = []
for pr_line in pr_obj.line_ids: #loop for multiple lines.
pr_lines.append((0,0, {
'purchase_request_id' : pr_obj.id,
'product_id' : pr_line.product_id.id,
'description' : pr_line.name,
'qty_transfer' : pr_line.product_qty,
'uom_id' : pr_line.product_uom_id.id,
}))
You can update your context like this-
context.update({
'default_warehouse_id': self.approving_matrix_id.warehouse.id,
'default_internal_transfer_receipt': pr_lines,
})
in the action you can pass context like
'context' : context,
result_ids = fields.Many2many("schoolresults.detail", "student_id", "School Results", default="_get_subjects")
def get_default_lines(self):
obj = self.env['your.class.name'].search([])
return obj
Try adding many2many field to your class.
Related
I have a field what displays an average score and depends on another model's field. I use SerializerMethodField in order to get needed value. It looks like this:
class TitleSerializer(serializers.ModelSerializer):
rating = serializers.SerializerMethodField()
class Meta:
fields = '__all__'
model = Titles
def get_rating(self, obj):
rating = obj.reviews.all().aggregate(Avg('score'))
return rating
It works but doesn't return it in a way I need. Now I get data what looks like:
"rating" : {
"score__avg" : some_value
}
How can I change it to:
"rating" : some_value
Thanks in advance.
You give the aggregate expression a name, so with a named parameter:
def get_rating(self, obj):
# returns {'rating': … }
return obj.reviews.aggregate(rating=Avg('score'))
or you can unwrap it out of the dictionary:
def get_rating(self, obj):
# returns … (value)
return obj.reviews.aggregate(Avg('score'))['score__avg']
I have a field object that I want to do filtering with
Since the filter field will change dynamically.
only_user=User.objects.first()
field_object = only_user._meta.get_field(field)
field_object2 = only_user._meta.get_field(field2)
QuerySet = User.objects.filter(Q(field_object="xxx")|Q(field_object2="yyy"))
But I couldn't do it, it said
Cannot resolve keyword 'field_object' into field. Choices are: .....
EDIT:
I figure it out, Thank You:
sub_filter = {field: field_value}
sub_filter = {field2: field_value}
q_list=[Q(**sub_filter),Q(**sub_filter2)]
QuerySet = User.objects.filter(
reduce(operator.or_, q_list)
)
You can't pass field_object directly in filter. Because you can't pass a variable as key of keyword argument.
Try like this:
field_object = only_candidate._meta.get_field(field)
query1 = { field_object : 'xxx' }
query2 = { field_object2 : 'yyy' }
QuerySet = MyModel.objects.filter(Q(**query1)|Q(**query2))
Basically here I am creating a dictionary using field_object, and key is field_object and value is the actual value you want to query. Then using **query I am converting the dictionary to key=value and passing it through filter.
I have 2 classes:
class my_request(models.Model):
_name = 'my.request'
_inherit = ['mail.thread', 'ir.needaction_mixin']
supply_ids = fields.Many2many(comodel_name='supply.conditions',
relation='purchase_supply_rel',
column1='purchase_requests_id', column2='supply_conditions_id',
string='Supply Conditions')
and
class SupplyConditions(models.Model):
_name = 'supply.conditions'
vendor = fields.Char(string='Vendor', required=True)
quantity = fields.Char(string='Quantity', required=True)
request_id = fields.Many2one('my.request', 'My request')
name = fields.Many2one('my.request.line', string='Product', required=True)
currency_id = fields.Many2one('res.currency', string='Currency', default=_get_default_currency)
supply_rel_id = fields.Boolean(string='Relation field', default=False)
my.request class part of form view xml:
<page string="Order">
<field name="supply_ids" domain="[('purchase_id', '=', id)]"/>
</page>
When I select values from my.request form view and click on Save button I call write() method and at that time insert m2m values (if there is selected values) into relational table (purchase_supply_rel).
I want to add check if purchase.request id is in purchase_supply_rel table in purchase_requests_id field when I click on Save button. My function. Changes after # my new code coment:
#api.multi
def write(self, vals):
res = super(PurchaseRequest, self).write(vals)
for request in self:
if request.state != 'draft':
if vals.get('assigned_to'):
self.message_subscribe_users(user_ids=[request.assigned_to.id])
# my new code
test = self.env['purchase.request'].search([(self.id,'in','supply_ids')])
_logger.warning("test " + str(test));
return res
But I got an error:
File "/opt/odoo/openerp/osv/expression.py", line 586, in check_leaf
raise ValueError("Invalid leaf %s" % str(self.leaf))
ValueError: Invalid leaf (348, 'in', 'supply_ids')
How else I can check this? And what I am doing wrong?
UPDATE:
I need to get test value smth like this: purchase_request(245,352)
245,352 - ids of purchase.request class
You can not add dynamic value in domain in place of field name.
Domain is a list of tuples. Domain contains three portions in tuple.
Domain:
[('field_name','operator','values')]
So dynamic value is not allowed in field name in domain.
#api.multi
def write(self, vals):
res = super(PurchaseRequest, self).write(vals)
for request in self:
if request.state != 'draft':
if vals.get('assigned_to'):
self.message_subscribe_users(user_ids=[request.assigned_to.id])
# my new code
test = self.env['purchase.request'].search([('supply_ids','in',self.ids)])
_logger.warning("test " + str(test));
return res
I'm tryin to override Flask-Admin's edit_form() in order to dynamically populate a SelectField. I managed to do so this way
class ProductForm(Form):
order = IntegerField('order')
name = TextField('name')
category = SelectField('category', choices=[])
image = ImageUploadField(label='Optional image',
base_path=app.config['UPLOAD_FOLDER_ABS'],
relative_path=app.config['UPLOAD_FOLDER_R'],
max_size=(200, 200, True),
endpoint='images',
)
class ProductsView(MyModelView):
create_template = 'admin/create-products.html'
edit_template = 'admin/edit-products.html'
column_list = ('order', 'name', 'category', 'image')
form = ProductForm
column_default_sort = 'order'
def edit_form(self, obj):
form = self._edit_form_class(get_form_data(), obj=obj)
cats = list(db.db.categories.find())
cats.sort(key=lambda x: x['order'])
sorted_cats = [(cat['name'], cat['name']) for cat in cats]
form.category.choices = sorted_cats
form.image.data = obj['image']
return form
The problem is now the form in the /edit/ view defaults name and order fields to empty unless i add these two lines to edit_form():
form.name.data = obj['name']
form.order.data = obj['order']
But if i do so the form will ignore every change (because i set form.field_name.data already?)
How do I preserve the old form values as "placeholders" while correctly overriding edit_form()?
I had a similar problem and thanks to the answer here I solved it;
Basically you set the default for the fields, and then call the individual fields process() method, passing in the currently set value.
from wtforms.utils import unset_value
form.name.default = obj['name']
form.name.process(None, form.name.data or unset_value)
form.order.default = obj['order']
form.order.process(None, form.order.data or unset_value)
I have an object:
POP_CULTURE_TYPES = (
('SG','Song'),
('MV', 'Movie'),
('GM', 'Game'),
('TV', 'TV'),
)
class Pop_Culture(models.Model):
name = models.CharField(max_length=30, unique=True)
type = models.CharField(max_length=2, choices = POP_CULTURE_TYPES, blank=True, null=True)
Then I have a function:
def choice_list(request, modelname, field_name):
mdlnm = get.model('mdb', modelname.lower())
mdlnm = mdlnm.objects.values_list(field_name, flat=True).distinct().order_by(field_name)
return render_to_response("choice_list.html", {
'model' : modelname,
'field' : field_name,
'field_list' : mdlnm })
This gives me a distinct list of all the "type" entries in the database in the "field_list" variable passed in render_to_response. But I don't want a list that shows:
SG
MV
I want a list that shows:
Song
Movie
I can do this on an individual object basis if I was in the template
object.get_type_display
But how do I get a list of all of the unique "type" entries in the database as their full names for output into a template?
I hope this question was clearly described. . .
How about something like this at the end of your choice_list()?
def choice_list(request, modelname, field_name):
# ...
pct = dict(POP_CULTURE_TYPES)
return [pct[key] for key in mdlnm]
Or in one line w/o the dict() call:
return [pct[1] for pct in POP_CULTURE_TYPES if pct in mdlnm]
Not pretty but it will work until to run across something better.
You could use:
OBJECT.get_FIELD_display()
Example:
content = Pop_Culture.objects.get(...)
ctype = content.get_type_display()
There is no need for workarounds :)