This question has been flagged
1 Reply
5278 Views

hi, i have created a module, of models department, subjects and students , department-->subjects-->students ,, when student  chooses subjects , student name should dynamically append in the department.here is my code


class Departments(osv.osv):

_name = 'department.department'

_description = 'department'

_columns={

'id':fields.integer('id',required=True),

'name' : fields.char(' Name', size = 16, required = True, translate = True),  

'subjects':fields.one2many('subject.subject','department','Subjects'),

'lecturer':fields.one2many('lecturer.lecturer','department','Lecturers',required=True),  

'student_name':fields.one2many('subject.subject','student_id','Students'), <----i think this line has to be modified--->

}


class Subjects(osv.osv):

_name = 'subject.subject'

_description = 'subject'

_columns={

'id':fields.integer('id',required=True),

'name' : fields.char(' Name', size = 16, required = True, translate = True),

'department':fields.many2one('department.department','Department',required=True),    

'student_id':fields.many2many('students.students','subject_student','subject_id','student_id',string='Students'),

}


class Student(osv.osv):

_name = 'students.students'

_description = 'student'

_columns={

'id':fields.integer('ID',required=True),

'name' : fields.char(' Name', size = 16, required = True, translate = True),      

'subject_id':fields.many2many('subject.subject','subject_student','student_id','subject_id',string='Subjects'),

}

Avatar
Discard
Best Answer

A few things:

Your naming convention needs some work.  First, you can get rid of the 'id' column in all three models - it's always created for you.  Also, the naming convention on models is generally less specific to more specific.  You also want to avoid plural model names (it's account.move.line, not account.move.lines!).  So in your case, better model names might be "school.department", "school.subject", and "school.student".  This makes it very clear where all of your school-related models are - everything is "school.something".  Also, unless you're going to flesh out the description to be a phrase or sentence, you might as well leave it blank.  It only makes things easier long-term when it comes to maintenance to AVOID _description, in my experience as a developer.  You could also consider just inheriting res.partner and adding the many2many field linking subjects and a "student" boolean rather than reinventing the wheel with a brand new model that does almost exactly what res.partner does.  Naming conventions also suggest that all many2one fields be named "something_id" and all one2many and many2many fields be named "something_ids".  It's not a hard requirement, but it's very beneficial to help clarify code.

Next, to do specifically what you're asking, there are two issues.  One, your subject_id field in the students.students model is many2many, but the student_name field is one2many.  one2many can only be the inverse of many2one.  many2many must be the inverse of many2many.  The big problem this causes is that you have links between students and subjects as well as subjects and departments, but not departments and students.  Also, as a practical matter, you need to consider the length the name can get if the student name is going to contain all of that information.

To accomplish this, you need to link departments to students, and your code needs to inherit/override the name_get() function on the student model.  Computing the student list per department is going to be a very slow process, as you're looking at nesting a loop inside a loop inside a loop - you may want this to be a stored value or to reconsider the model structure!  This also becomes a costly process to modify names on the fly, so you may want to only enable this name change using a context flag.  Here's what the Python code looks like:

class school_department(osv.osv):
    _name = 'school.department'

    def _get_students(self, cr, uid, ids, fields, args, context=None):
        """For each department, find all subjects, then all students in those subjects"""
        if context is None: context = {}
        res = {}
        # Need to account for duplicates in students - the same student may have multiple subjects
        # in the same department.
        for department_rec in self.browse(cr, uid, ids, context=context):
            all_student_ids = []
            for subject_rec in department_rec.subject_ids:
                for student_rec in subject_rec.student_ids:
                    all_student_ids.append(student_rec.id)
            # Quickly remove duplicate students and assign to the final result dictionary
            res[department_rec.id] = list(set(all_student_ids))
        return res

    _columns = {
        'name': fields.char('Name', size=16, required=True, translate=True),
        'subject_ids': fields.one2many('school.subject', 'department_id', 'Subjects'),
        'lecturer_ids': fields.one2many('school.lecturer', 'department_id', 'Lecturers', required=True),
        'student_ids': fields.function(_get_students, type='many2many', string='Students'),
    }


class school_subject(osv.osv):
    _name = 'school.subject'
    _columns = {
        'name': fields.char('Name', size=16, required=True, translate=True),
        'department_id': fields.many2one('school.department', 'Department', required=True),
        'student_ids': fields.many2many('school.student', 'school_subject_student_rel', 'subject_id', 'student_id', string='Students'),
    }


class school_student(osv.osv):
    _name = 'school.student'
    _columns = {
        'name': fields.char('Name', size=16, required=True, translate=True),
        'subject_ids': fields.many2many('school.subject', 'school_subject_student_rel', 'student_id', 'subject_id', string='Subjects'),
    }

    def name_get(self, cr, uid, ids, context=None):
        """Return the student name or the student name and all departments"""
        if context is None: context = {}
        # If the context flag isn't present, just return the student's name
        if not context.get('show_departments'):
            return super(school_student, self).name_get(cr, uid, ids, context=context)
        res = []
        for student_rec in self.browse(cr, uid, ids, context=context):
            # Avoid duplicating departments
            student_department_ids = []
            student_department_names = []
            for subject_rec in student_rec.subject_ids:
                if not subject_rec.department_id.id in student_department_ids:
                    student_department_ids.append(subject_rec.department_id.id)
                    student_department_names.append(subject_rec.department_id.name)
            # Make sure we're not trying to force name display changes if the student hasn't
            # signed up for any classes yet.
            full_student_name = student_rec.name
            if student_department_ids:
                full_student_name = '%s [%s]' % (full_student_name, ', '.join(student_department_names))
            # Result must be a list of tuples: (id, name string)
            res.append((student_rec.id, full_student_name))
        return res

Also, here's a snippet of a view definition that includes the context information to properly activate the name_get() changes:

<field name="student_ids" context="{'show_departments':True}"/> 
Avatar
Discard
Author

Thank you for your response,it helped me a lot.