Django Auth : How are many-to-many relationship models created? - django

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)

Related

How to design the user table in database?

I want to develop a server side of an app that holds users.
Of course I need a table in database holding the user information.
At first I may write
class User(models.Model): # using django models
userid = ...
password = ...
which gives me a database table containing userid and password.
However, I might want to add some attributes (maybe Credit, Birthday...so on) to each user in the future. I just can't think up all of them right now. And I can't know what attributes I would really need in the future.
How can I deal with it?
There's already a user table in Django. This table is automatically create when you first apply the migration with 'manage.py migrate' command.
In database schema, this table is listed as auth_user and you can import it into Django with the following command
from django.contrib.auth.models import User
Django provides a default model for User. you can use it like this.
from django.contrib.auth.models import User
and as per your second query. you can do so by creating another model and adding a ForeignKey or OneToOneField of User model to link it with each user.
class Customuserprofile(models.Model):
user=models.OneToOneField(settings.AUTH_USER_MODEL)
credit=models.CharField()
birthday=models.DateTimeField()
well, if you want to add some field to the User you can use AbstractUser or AbstractBaseUser model read this that explain the differences and give a example https://simpleisbetterthancomplex.com/tutorial/2016/07/22/how-to-extend-django-user-model.html#abstractbaseuser

In django, for a user, get all objects which ForeignKey to groups he/she belongs to (efficiently)

I have 3 models, User, Group and Exam:
from django.db import models
from django.contrib.auth.models import User, Group
class Exam(models.Model):
# a lot of fields
solutions_group = models.ForeignKey(Group)
For an instance user of model User I want to get all Exams in whose solutions_group user belongs.
This is what I thought of:
exam_list = []
for group in user.groups.all():
exam_list+= list(group.exam_set.all())
But this involves multiple database queries.
Is there a better way to do this, involving lesses queries?
I'm using Python 3.4 and Django 1.8.
Try filtering on Exam.objects using an "in" filter
user_groups = user.groups.all()
user_exams = Exam.objects.filter(solutions_group__in=user_groups)
I think you can also use the related_name from group to user (user_set) using the following query:
user_exams = Exam.objects.filter(solutions_group__user_set=user)

Migrate model while adding user foreign key

So i have this model where I added user field as foreign key to User model
from django.db import models
from django.contrib.auth.models import User
from django.conf import settings
class Post(models.Model):
url = models.URLField()
title = models.CharField(max_length=140)
user = models.ForeignKey(settings.AUTH_USER_MODEL)
Than I run
python3 manage.py makemigrations
And get:
You are trying to add a non-nullable field 'user' to comment without a default;
we can't do that (the database needs something to populate existing rows).
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows)
2) Quit, and let me add a default in models.py
I understand that I need to give it default user but I don't get how to do that.
P.S. Django version - 1.8.3
Ok, so it happens migration utility's question hadn't appeared intuitive enough for me. What migration utility was really asking were required fields of User model. So basically you need to type in some data 3 times (for username, password and email). The problem is utility isn't really saying what field is it expecting.

Can't access users in admin after migration

So I successfully migrated from a profile model to an extended User model. The data migration all worked fine, but I can't access my users from the admin, I get the following error:
DatabaseError: (1146, "Table 'mydb.app_myuser_groups' doesn't exist")
All I have defined in models.py is the following:
class MyUser(AbstractUser):
isSpecial = models.BooleanField(default=True)
having followed these instructions. Is there more I need to do to get this to work?
See my previous answer here and modify step 4 to look like this:
# encoding: utf-8
from south.db import db
from south.v2 import SchemaMigration
class Migration(SchemaMigration):
def forwards(self, orm):
# Fill in the destination name with the table name of your model
db.rename_table('auth_user', 'accounts_user')
db.rename_table('auth_user_groups', 'accounts_user_groups')
def backwards(self, orm):
db.rename_table('accounts_user', 'auth_user')
db.rename_table('accounts_user_groups', 'auth_user_groups')
models = { ....... } # Leave this alone
AbstractUser inherits from PermissionMixin, which has a ManyToManyField to the Group model. So there should be a app_myuser_groups table in the database. South may be able to create the intermediate table, but I don't know how. What I know is that syncdbing after having removed app_myuser should work, even though your migration would be shredded.
This question about adding a through table in a migration should give you more insight.

How to create a permissions table independent of the user permissions in Django?

I want to create a permission table that takes django-users and another table as its foreign key . And then gives permissions to it . What should be there in models.py ?
The doubt can be put across as two separate questions :
How to use django-users (user id) as a foreign key in another app called
permissions .
How to use table-id that is generated by django when syncdb is
done as the priamry key of
that table (Different app) , to be used as foreign key of this app permissions .
Is there a reason you can't use the permissions features in django.contrib.auth? By using the permissions feature of the Meta object and the Groups table, you can easily create a matrix of "users in group X may perform action Y".
The more I think about this, the more it seems that your implementation would mirror the Groups/permissions feature without extra benefits.
See https://docs.djangoproject.com/en/1.3/topics/auth/ for details.
Your best best is to create a User Profile - which is a model that can be linked to the User model, where the Profile model contains whatever keys you want. Then you can call User.get_profile(), and get a model object, check what keys it has.
For the model, you want to look at the ContentTypes app in .contrib. (https://docs.djangoproject.com/en/1.3/ref/contrib/contenttypes/) and you can just create a foreign key to the user.
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
class Permission(models.Model):
user = models.ForeignKey(User)
table = models.ForeignKey(ContentType)
### the rest of the model.
Then in your view or whatever:
perm = Permission()
perm.user = request.user
perm.table = ContentType.objects.get_for_model(TableToAddPermissionFor)
perm.save()