Django 1.7 - Adding permissions programmatically does not work as expected - django

What is going on here ?
> from django.contrib.auth.models import Permission
> from django.contrib.contenttypes.models import ContentType
> p = Permission.objects.filter(
content_type = ContentType.objects.get_for_model(Transaction)
).get(
codename = 'add_transaction'
)
> user.user_permissions.add(p)
> user.user_permissions.all()
[<Permission: myapp | Transaction | Can add Transaction>]
> user.get_all_permissions()
set([])
> user.has_perm('add_transaction')
False
> user.has_perm('myapp.add_transaction')
False
am i missing a save here somewhere ?

The ModelBackend caches the permissions on the user object. The Django docs suggest that you reload the user from the db after changing the permissions.
user.user_permissions.add(p)
user = User.objects.get(pk=user.pk)

dgel's answer is correct but incomplete. Both _perm_cache and _user_perm_cache need to be cleared to force a full reload.
for attr in ('_perm_cache', '_user_perm_cache'):
delattr(user, attr)

I believe user instances have a permissions cache that is populated when it is first pulled from the DB. Try deleting the cache before calling has_perm:
delattr(user, '_perm_cache')
After that, has_perm should work as expected. Normally this isn't an issue as you rarely add then immediately test for permissions on the same user instance (without pulling it from the DB again). Definitely can trip you up when you're testing in the python shell though.

Related

Why Django auto adds permissions to group?

Currently I'm making some groups for my backend and I noticed through the admin panel that the groups have some extra permissions I did not add. What is the cause of this behavior?
models.py:
produccion_group, created = Group.objects.get_or_create(name="Produccion y cuentas")
produccion_group.permissions.add(
Permission.objects.get(codename='add_brand'),
Permission.objects.get(codename='change_brand'),
Permission.objects.get(codename='view_brand'),
Permission.objects.get(codename='add_expense'),
Permission.objects.get(codename='change_expense'),
Permission.objects.get(codename='view_expense'),
)
produccion_group.save()
Admin panel:
The problem is solved by adding the following block of code. Still doesn't answer the incognita of why it's added automatically, but fixes the unwanted permissions.
def ready(self):
produccion_group.permissions.remove(
Permission.objects.get(codename='add_user'),
Permission.objects.get(codename='change_user'),
Permission.objects.get(codename='delete_user'),
Permission.objects.get(codename='view_user'),
Permission.objects.get(codename='add_group'),
Permission.objects.get(codename='change_group'),
Permission.objects.get(codename='delete_group'),
Permission.objects.get(codename='view_group'),
)
produccion_group.save()

Django user has_perm returning false even though group has permission

I'm running into a problem where I've created a Group with certain permissions, and successfully added a user to that group, but when I check user.has_perm with that permission, I'm getting False.
I've even tried things like saving the user and re-fetching them from the database to avoid caching issues, but it makes no difference.
The terminal output below should give an idea of what's happening
# Get a group from the database and check its permissions
> special_group = Group.objects.get(name="special_group")
> a_perm = special_group.permissions.all()[0]
> a_perm.codename
'some.permission'
# Get a user from that group and check if they have those permissions
> a_user = special_group.user_set.all()[0]
> a_user.has_perm(a_perm.codename)
False
> a_perm.codename in a_user.get_group_permissions()
False
# Curiously, get_group_permission sort of returns what we need
> a_user.get_group_permissions()
{'ContentType object.some.permission'}
# If we essentially coax the group_permissions into a list and pull an item out of there, has_perm returns true, but this string is different to a_perm.codename
> a_user.has_perm(list(a_user.get_group_permissions())[0])
True
Does anyone know why has_perm isn't behaving as I'm expecting? We are using Django 1.10.3.

Does django-constance admin supports database backend?

I'm trying to setup the admin to show settings meant to be stored in database backend (Postgres 9.5.0). I manually created values in shell_plus as follows:
In [1]: from constance.backends.database.models import Constance
In [2]: first_record = Constance.objects.get(id=1)
In [3]: first_record
Out[3]:
pg-admin properly shows the entry although django admin doesn't show it at all. I ran migrate command for both databases (I have default and product databases) but the record still is not showing up. Certainly I can make it work with forcing to register with admin as follows:
admin.site.register(Constance)
but my question is if it's necessary?
Yes, they do.
You need to manage dependencies, but you can just use next command to install:
pip install "django-constance[database]"
Also you need to add some additionl settings to your settings.py :
CONSTANCE_BACKEND = 'constance.backends.database.DatabaseBackend'
INSTALLED_APPS = (
# other apps
'constance.backends.database',
)
#optional - in case you want specify table prefix
CONSTANCE_DATABASE_PREFIX = 'constance:myproject:'
Then you need to apply migrations by running command python manage.py migrate database
For displaying settings inputs in admin you should specify them in your settings.py. There are various types of fields and you even can add your own types of fields using CONSTANCE_ADDITIONAL_FIELDS parameter.
CONSTANCE_CONFIG = {
'THE_ANSWER': (42, 'Answer to the Ultimate Question of Life, '
'The Universe, and Everything'),
}
You can read more at documentation page.

Getting django-facebook to work

I an trying to get django-facebook to work as per instructions given in the readme on
https://github.com/tschellenbach/Django-facebook. I am new to django.
It looks simple but I am facing the following problems. I am not able to get it to work.
In the readme it says AUTH_USER_MODEL = 'member.FacebookUser'. I am guessing the right option is
AUTH_USER_MODEL = 'django_facebook.FacebookUser'
after importing the models - this took me some t even after making that change, syncdb throws an error stating that:
FacebookUser does not have a USERNAME_FIELD.
Not able to solve that I decided to use the default user model - auth.user. That works and I was able to load facebook/example. However after authentication from facebook, I get an error
You need to set AUTH_PROFILE_MODULE in your project settings
So I added AUTH_PROFILE_MODULE = 'django_facebook.FacebookProfile'
Now it returns a new error -
FacebookProfile matching query does not exist. Lookup parameters were {'user_id_exact': 2L}
What should I do now?
Do python manage.py syncdb or whatever it needs to be done to update your database schema with the new table (facebook_profile).
Also, you don't mention it but I have and app that uses django_facebook and I have my settings.py file like this:
TEMPLATE_CONTEXT_PROCESSORS = (
...
'django_facebook.context_processors.facebook',
...
)
AUTHENTICATION_BACKENDS = (
...
'django_facebook.auth_backends.FacebookBackend',
)
I hope it helps

After adding a permission, why do I need to re-query for the Django user to see the added permission?

In the following Django manage.py shell session, why don't I see the permission that I added to my user object until I do a new query for that user?
In [16]: john = User.objects.create_user(username='john')
In [17]: john
Out[17]: <User: john>
In [18]: john.get_all_permissions()
Out[18]: set([])
In [19]: john.user_permissions.add(Permission.objects.get(codename='add_user'))
In [20]: john.get_all_permissions()
Out[20]: set([]) <== I expected to see the new permission here. Why not?
In [21]: john = User.objects.get(username='john')
In [22]: john.get_all_permissions()
Out[22]: set([u'auth.add_user'])
Django does caching of permissions the first time you request them for a given user instance, it stores them against _perm_cache on the user object, this is to prevent excessive database lookup when checking permissions which might happen numerous times during a request (especially during template rendering where you might have permission checks around forms and links).
You've identified one solution to your problem, which is reloading the user from the database. the other is to delete _perm_cache (and _group_perm_cache if you want to be thorough) from the user object:
del john._perm_cache
del john._group_perm_cache