django.contrib.auth.models
have only 3 models, User
, Group
, Permission
, as per the doc and code.
Doc does say groups
and permissions
are many-to-many relationships.
But when we migrate
how come other relations user_group
, user_permissions
, group_permissions
are created?
The tables for ManyToMany relationship are created by lines with models.ManyToManyField
string that are in the migration file.
In this case they are created by the initial migration django/contrib/auth/migrations/0001_initial.py
. It is very similar to django/contrib/auth/models.py
.
The related SQL commands can be read by
$ python manage.py sqlmigrate auth 0001_initial
See the lines:
CREATE TABLE "auth_user_groups"...
CREATE TABLE "auth_group_permissions"...
CREATE TABLE "auth_user_user_permissions"...
Especially interesting two texts are around the words REFERENCES
. That are the foreign keys to base tables, e.g.:
... "user_id" integer NOT NULL REFERENCES "auth_user" ("id") ...
EDIT:
You probably know a ManyToMany relationship joined by an explicit intermediate model specified by the parameter through=SomeModel with an extra field.
I explain it in example how you can use a hidden intermediate table in your code.
Imagine that you want to make a note to some permission why you added it to some person.
You can explore the model structure from command line, because in "auth/models.py" you could read only the name "user_parmissions".
>>> User._meta.local_many_to_many
[<django.db.models.fields.related.ManyToManyField: groups>,
<django.db.models.fields.related.ManyToManyField: user_permissions>]
>>> User.user_permissions.through
<class 'django.contrib.auth.models.User_user_permissions'>
>>> User.user_permissions.through._meta.local_fields
[<django.db.models.fields.AutoField: id>,
<django.db.models.fields.related.ForeignKey: user>,
<django.db.models.fields.related.ForeignKey: permission>]
your_app/models.py
from django.contrib.auth.models import User, Group, Permission
from django.db import models
# This line is instead of import, because the implicit intermediate table
# can not be imported.
UserPermission = User.user_permissions.through
class UserPermissionReason(models.Model)
user_permission = models.ForeignKey(UserPermission, ...)
reason = models.CharField(help_text="Why I added that permission", ...)
makemigrations, migrate, shell...
Example: Create a user permission with your note
>>> from your_app.models import UserPermissionReason, UserPermission
>>> from django.contrib.auth.models import User, Group, Permission
>>> permission = Permission.objects.get(name='Can add user',
content_type__app_label='auth')
>>> permission
<Permission: auth | user | Can add user>
>>> user_permission = UserPermission.objects.create(user=me_superuser,
permission=permission)
>>> why = UserPermissionReason.objects.create(
user_permission=user_permission,
reason="Because I can :-)"
)
If you don't need to reference the intermediate table you would use a normal simple some_ruser.user_permissions.add(permission)