This example is based on article Django Bootstrap3 Example, it reuse the Department and Employee model class. Suppose Department and Employee model are many to many relationship, that means one department can has multiple employee and one employee can belongs to multiple department. Then we need to do following changes.
1. Change models.py File.
Just change the Employee model class definition in DjangoHelloWorld / dept_emp / models.py file. We set Employee model class’s dept field type to models.ManyToManyField(Department) now.
# create Employee model, this model will be mapped to table user_register_login_employee in sqlite db. class Employee(models.Model): # Employee has two foreign key, user(Django built-in auth_user table) and department. user = models.ForeignKey(User, on_delete=models.CASCADE,) # support department and employee table are many to many relationship. dept = models.ManyToManyField(Department) emp_mobile = models.CharField(max_length=100) # should add the () after IntegerField, otherwise the emp_salary column will not be created. emp_salary = models.IntegerField() # if do not specify the auto_now=True attribute with value then this field can not be created. emp_onboard_date = models.DateTimeField(auto_now=True) # this method will be called when other model reference Employee model as foreign key. def __str__(self): return self.user.username + ',' + str(self.emp_salary) + ',' + str(self.emp_mobile) class Meta: unique_together = ['emp_mobile']
2. Change admin.py File.
We also need to change the EmployeeAdmin model admin class in admin.py file, we need to remove ‘dept’ from the EmployeeAdmin‘s list_display list values. Otherwise you will get bellow error.
ERRORS:
<class ‘dept_emp.admin.EmployeeAdmin’>: (admin.E109) The value of ‘list_display[2]’ must not be a ManyToManyField.
DjangoHelloWorld / dept_emp / admin.py
class EmployeeAdmin(admin.ModelAdmin): # a list of displayed columns name, the user and dept foreign key will display related model's __str__(self) method returned string value. list_display = ['id', 'user', 'emp_mobile','emp_salary','emp_onboard_date'] # Register your models to admin site, then you can add, edit, delete and search your models in Django admin site. admin.site.register(Employee, EmployeeAdmin)
3. Migrate The New Models To Create Related Database Tables.
After model class make changes, we need to migrate the changes to back-end database tables follow below steps.
- Open terminal and go to DjangoHelloWorld project root folder.
- Execute
$ python3 manage.py makemigrations dept_emp
in terminal to get the model class changes. - Then run
$ python3 manage.py migrate dept_emp
to apply the model class changes to database. - Use your favorite SQLite3 database file explorer to open sqlite3.db file in the DjangoHelloWorld project root folder.
- Then you will find there are three tables has been created, they are dept_emp_department, dept_emp_employee and dept_emp_employee_dept.
4. Populate Test Data.
Before continue, we need to add some test data for demo usage.
- Add below users in this django project admin web site like below.
- Add three departments in the django project admin web site also.
- Add some employees also, please note the employee is identified by mobile number ( each employee mobile number is unique ), and there are two employee use same user jack. And each employee can attend multiple departments.
5. Operate On Model Class Foreign Key Field.
From above Employee model class definition, we see that it has a foreign key type field, and the foreign key model class is django.contrib.auth.models.User. We can manage the foreign key field as below.
5.1 Retrieve Employee Foreign Key Field Information.
- Open terminal and go to Django project root folder, then run
$ python3 manage.py shell
command to go to django shell console. - Run below command to get employee user information.
>>> from dept_emp.models import Employee >>> Employee.objects.get(id=1) <Employee: jack,100000,13901234567> >>> emp = Employee.objects.get(id=1) >>> emp.user <User: jack> >>> emp.user.email '[email protected]'
4.2 Get Same User Mapped Employee Objects.
- In Django shell console run below command, we can get the multiple Employee objects that use same user. Please note the user.employee_set attribute usage, this is a QuerySet object which can run all QuerySet functions. Because User is foreign key of Employee, so the user object has the employee_set attribute, this is done by Django automatically.
>>> from django.contrib.auth.models import User >>> user = User.objects.get(username='jack') >>> user.employee_set.all() <QuerySet [<Employee: jack,100000,13901234567>, <Employee: jack,100000,13690909099>]> >>> user.employee_set.all().filter(emp_mobile=13690909099) <QuerySet [<Employee: jack,100000,13690909099>]>
6. Operate On Model Class Many To Many Field.
6.1 Get One Employee Related Departments.
>>> from dept_emp.models import Department, Employee >>> emp = Employee.objects.get(emp_mobile=1369090909) >>> emp.dept.all() <QuerySet [<Department: Quality Assurance,Responsible for company website quality and test.>, <Department: Market,Market plan and implement.>]> >>> emp.dept.filter(dept_name__contains='Market') <QuerySet [<Department: Market,Market plan and implement.>]>
6.2 Add A New Department To Employee.
>>> from dept_emp.models import Department, Employee >>> emp = Employee.objects.get(emp_mobile=1369090909) >>> emp.dept.add(Department.objects.create(dept_name='IT', dept_desc='Information tech support')) >>> emp.dept.all() <QuerySet [<Department: Quality Assurance,Responsible for company website quality and test.>, <Department: Market,Market plan and implement.>, <Department: IT,Information tech support>]>
6.3 Create A New Employee Then Add A New Department To It.
In [1]: from dept_emp.models import Employee, Department In [2]: from django.contrib.auth.models import User In [3]: emp = Employee.objects.create(user=User.objects.get(username='jerry'), emp_mobile=13919283928, emp_salary=80000, sex='F') In [4]: emp.dept.add(Department.objects.create(dept_name='Market',dept_desc='Markete department.'))
Because Employee and Department are many to many relationship, so you can not set dept when create the Employee, you can add Department after emp has been created otherwise you will encounter below errors.
In [5]: emp = Employee.objects.create(user=User.objects.get(username='jerry'), dept=Department.objects.get(dept_name='Market'), emp_mobile=13919283928, emp_salary=80000, sex='F') --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-5-0bfb7fc93e05> in <module>() ----> 1 emp = Employee.objects.create(user=User.objects.get(username='jerry'), dept=Department.objects.get(dept_name='Market'), emp_mobile=13919283928, emp_salary=80000, sex='F') ~/anaconda3/lib/python3.6/site-packages/django/db/models/manager.py in manager_method(self, *args, **kwargs) 80 def create_method(name, method): 81 def manager_method(self, *args, **kwargs): ---> 82 return getattr(self.get_queryset(), name)(*args, **kwargs) 83 manager_method.__name__ = method.__name__ 84 manager_method.__doc__ = method.__doc__ ~/anaconda3/lib/python3.6/site-packages/django/db/models/query.py in create(self, **kwargs) 409 and returning the created object. 410 """ --> 411 obj = self.model(**kwargs) 412 self._for_write = True 413 obj.save(force_insert=True, using=self.db) ~/anaconda3/lib/python3.6/site-packages/django/db/models/base.py in __init__(self, *args, **kwargs) 477 if prop in property_names or opts.get_field(prop): 478 if kwargs[prop] is not _DEFERRED: --> 479 _setattr(self, prop, kwargs[prop]) 480 del kwargs[prop] 481 except (AttributeError, FieldDoesNotExist): ~/anaconda3/lib/python3.6/site-packages/django/db/models/fields/related_descriptors.py in __set__(self, instance, value) 535 raise TypeError( 536 'Direct assignment to the %s is prohibited. Use %s.set() instead.' --> 537 % self._get_set_deprecation_msg_params(), 538 ) 539 TypeError: Direct assignment to the forward side of a many-to-many set is prohibited. Use dept.set() instead.
6.4 Remove Employee Department.
>>> emp.dept.remove(Department.objects.get(dept_name='Market')) >>> emp.dept.all() <QuerySet [<Department: Quality Assurance,Responsible for company website quality and test.>, <Department: IT,Information tech support>]>
6.5 Get Same Department’s Employees.
>>> dept = Department.objects.get(dept_name='Develop') >>> dept.employee_set.all() <QuerySet [<Employee: jack,100000,13901234567>, <Employee: richard,100000,1369090908>]>
6.6 Add One Exist Employee To Department.
>>> dept.employee_set.add(Employee.objects.get(emp_mobile=1369090909)) >>> dept.employee_set.all() <QuerySet [<Employee: jack,100000,13901234567>, <Employee: richard,100000,1369090908>, <Employee: jerry,80000,1369090909>]>
6.7 Add A New Employee To Department.
>>> dept.employee_set.add(Employee.objects.create(user=User.objects.get(username='tom'), emp_mobile=13901234568, emp_salary=10000)) >>> dept.employee_set.all() <QuerySet [<Employee: jack,100000,13901234567>, <Employee: richard,100000,1369090908>, <Employee: jerry,80000,1369090909>, <Employee: tom,10000,13901234568>]>
6.8 Remove Employee From Department.
>>> dept.employee_set.remove(Employee.objects.get(user = User.objects.get(username='richard'))) >>> dept.employee_set.all() <QuerySet [<Employee: jack,100000,13901234567>, <Employee: jerry,80000,1369090909>, <Employee: tom,10000,13901234568>]>