i have problem with groups and permission in django.
I want to create a group of users and admin.
In django panel admin i see permission which i need:
app | user | Can add user
app | user | Can change user
app | user | Can delete user
app | user | Can view user
I need create group "user" and "admin" then add perm to user "Can view user" and to admin all this perms and this is need to do with migration.
My already code i suposed is totally stupid
# Generated by Django 4.0.3 on 2022-09-11 10:33
from django.db import migrations, transaction
from django.contrib.auth.models import Group, Permission
def add_group_permissions(a,b):
group, created = Group.objects.get_or_create(name='user')
try:
with transaction.atomic():
group.permissions.add(can_view_user)
group.save()
except InterruptedError:
group.delete()
class Migration(migrations.Migration):
dependencies = [
('app', '0001_initial'),
]
operations = [
migrations.RunPython(add_group_permissions),
]
If anyone can help, i need that.
I have some examples of custom permissions.
In your models.py try this below your class:
class Meta:
permissions = [
("access_app", "Can access the budget app"), # access for all budget app
(
"level_2",
"Can access the budget app - level 2",
), # access to EyePlan model + hr
(
"level_3",
"Can access the budget app - level 3",
), # access to EyePlan model only
]
In your views.py:
class BudgetMixin(PermissionRequiredMixin):
permission_required = "budget.access_app"
Try to customize this code for your own requirements, hope it gonna help you.
Ok, i have that now in models.py:
from django.db import models
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
username = models.EmailField(unique=True)
name = models.CharField(max_length=64)
last_name = models.CharField(max_length=64)
phone_number = models.CharField(max_length=9, unique=True)
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = []
class Meta:
permissions = [
("can_add_user", "Can add user"),
("can_change_user", "Can change user"),
("can_delete_user", "Can delete user"),
("can_view_user", "Can view user"),
]
But how to create now group "user" wtih permission can_view_user and "admin" with: can_add_user; can_change_user; can_delete_user; can_view_user.
Related
My Profile model has a OneToOne relation with Django's built-in User model.
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
verified = models.BooleanField(default=False)
If I want to change user's password or properties like Active or Superuser I have to do it in one Change User page, and to edit verified property I have to go to another.
Is there any way to merge this:
And this:
Into one form so I can edit everything about a user in one page?
Edit 1:
As you guys suggested the StackedInline approach, let's see how it turns out.
Please first look at Django's default Admin site (first screenshot above):
Everything is grouped in sections and sections have titles.
Look at how the password information is displayed.
There's a link to change the password.
Now I implement the StackedInline solution.
Please note that this is in the admin.py of my myapp:
from django.contrib import admin
from .models import Profile
from django.contrib.auth.models import User
# Register your models here.
class ProfileInline(admin.StackedInline):
model = Profile
class UserAdmin(admin.ModelAdmin):
inlines = (ProfileInline, )
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
Now let's look at Admin site:
Everything is scattered. Sections and their titles are gone (Personal info, Permissions, etc).
Password field shows the hashed password. All other information is gone.
There's no link to change the password.
Edit 2:
To solve the problem of Edit 1 I look at the source code of Django (https://github.com/django/django/blob/main/django/contrib/auth/admin.py) and add update my code as below:
class UserAdmin(admin.ModelAdmin):
inlines = (ProfileInline, )
fieldsets = (
(None, {"fields": ("username", "password")}),
(("Personal info"), {"fields": ("first_name", "last_name", "email")}),
(
("Permissions"),
{
"fields": (
"is_active",
"is_staff",
"is_superuser",
"groups",
"user_permissions",
),
},
),
(("Important dates"), {"fields": ("last_login", "date_joined")}),
)
add_fieldsets = (
(
None,
{
"classes": ("wide",),
"fields": ("username", "password1", "password2"),
},
),
)
filter_horizontal = (
"groups",
"user_permissions",
)
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
Now I have two sections in the Admin site:
The section on the top shows almost everything (except that the password field is still different and there's no link to change the password and also the verified field is not there) but sections and titles are back.
Then there's this additional and completely unnecessary part:
As you can see:
All fields of information about the user is repeated
Look at the password field
Information is not grouped in sections with titles
verified filed appears.
OP can use one of the InlineModelAdmin objects such as StackedInline. This allows one to create inline forms providing one the ability to edit models on the same page as a parent model.
Adapting for OP's case, it would be something like
from django.contrib import admin
class ProfileInline(admin.StackedInline):
model = Profile
class UserAdmin(admin.ModelAdmin):
inlines = [
ProfileInline,
]
admin.site.register(User, UserAdmin)
Now OP's admin site is set up to edit Profile objects inline from the User detail page.
User model Extension vs Inheritance.
Your profile model only add some elements to user. In this case Model inheritance can be better.
# models.py
class Profile(user):
verified = models.BooleanField(default=False)
after that you can achieve all fields for user and for profile:
# admin.py
class ProfileAdmin(ModelAdmin):
fields = '__all__'
if you don't want to switch on Model inheritance.
You can use InlineModel in UserAdmin to change related model.
# admin.py
class ProfileInline(StackedInline):
model=Profile
class UserAdmin(ModelAdmin):
inlines = (ProfileInline, )
you can use override AbstractUser from model Django so you can merge user in one place like this:
from django.contrib.auth.models import AbstractUser
from django.db import models
class CustomUser(AbstractUser):
verified = models.BooleanField(default=False)
and you need to add this line to Django settings.py file so that Django knows to use the new User class:
AUTH_USER_MODEL = 'users.CustomUser'
then make sure to migrate :
(env)$ python manage.py makemigrations
(env)$ python manage.py migrate
I'm getting this error.
ERRORS: subscriptions.StripeCustomer.user: (fields.E301) Field defines
a relation with the model 'auth.User', which has been swapped out.
HINT: Update the relation to point at 'settings.AUTH_USER_MODEL'.
I'm trying to configure Django Stripe Subscriptions following this manual https://testdriven.io/blog/django-stripe-subscriptions/
My models.py
from django.contrib.auth.models import User
from django.db import models
class StripeCustomer(models.Model):
user = models.OneToOneField(to=User, on_delete=models.CASCADE)
stripeCustomerId = models.CharField(max_length=255)
stripeSubscriptionId = models.CharField(max_length=255)
def __str__(self):
return self.user.username
My admin.py
from django.contrib import admin
from subscriptions.models import StripeCustomer
admin.site.register(StripeCustomer)
My settings.py
#used for django-allauth
AUTH_USER_MODEL = 'accounts.CustomUser'
DEFAULT_AUTO_FIELD='django.db.models.AutoField'
SITE_ID = 1
AUTHENTICATION_BACKENDS = (
'allauth.account.auth_backends.AuthenticationBackend',
'django.contrib.auth.backends.ModelBackend',
)
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
ACCOUNT_EMAIL_VERIFICATION = "none"
accounts/models.py
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
class Meta:
verbose_name_plural = 'CustomUser'
After setting above, I executed "python manage.py makemigrations && python manage.py migrate" then the error occurred.
I just mentioned the above settings in this question but still if more code is required then tell me I'll update my question with that information. Thank you
You have your OneToOneField pointing to the User model from django.contrib.auth when in fact you are using a custom user model CustomUser, hence you get the error. Generally if one wants to have a foreign key or any related field with the user model one should point it to settings.AUTH_USER_MODEL as described in the Referencing the User model [Django docs] so that such issues can be prevented easily. Hence change your StripeCustomer model like so:
from django.conf import settings
from django.db import models
class StripeCustomer(models.Model):
user = models.OneToOneField(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
stripeCustomerId = models.CharField(max_length=255)
stripeSubscriptionId = models.CharField(max_length=255)
def __str__(self):
return self.user.username
I have two different app that has separate models that inherits from AbstractionBaseUser like below
# in doctor/models.py
...
class Patient(AbstractBaseUser):
email = models.EmailField(blank=True, unique=True)
phone_number = models.IntegerField(blank=False, unique=True)
USERNAME_FIELD = 'phone_number'
REQUIRED_FIELD = ['phone_number', 'email']
...
# in Patient/models.py
...
class Patient(AbstractBaseUser):
email = models.EmailField(blank=True, unique=True)
phone_number = models.IntegerField(blank=False, unique=True)
USERNAME_FIELD = 'phone_number'
REQUIRED_FIELD = ['phone_number', 'email']
...
Both models have different fields
# in settings.py
AUTH_USER_MODEL = [
'doctor.Doctor',
'patient.Patient'
]
I tried to do this but in making migration it tells me that it must be a single model
AssertionError: ForeignKey(['doctor.Doctor', 'patient.Patient']) is invalid. First parameter to ForeignKey must be either a model, a model name, or the string 'self'
I read docs but I couldn't find any help
How can I fix this and how can I have multiple AUTH_USER_MODEL
how can I have multiple AUTH_USER_MODEL.
You don't. There is one user model. It would also make all procedures more complicated. What if a the same email address occurs in both the Doctor and Patient model? Then how would you log in such person?
You can however make multiple extra models with a ForeignKey to the user model. We can for example use the default user model:
# settings.py
# …
AUTH_USER_MODEL = 'auth.User'
# …
You can of course also specify a basic user model yourself. But it should be one user model.
Then both the Doctor and Patient model can have a one-to-one relation to the User model:
from django.db import models
from django.conf import settings
class Doctor(models.Model):
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE
)
# … extra doctor fields …
as well as a patient:
from django.db import models
from django.conf import settings
class Patient(models.Model):
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE
)
# … extra patient fields …
You can then check for a user if it is a doctor by accessing the .doctor attribute:
from django.contrib.auth.decorators import login_required
#login_required
def some_view(request):
try:
doctor = request.user.doctor
except AttributeError:
# … user is not a doctor …
pass
pass
This also allows that a User is both a Doctor and a Patient for example. This may look odd at first. But later it is possible that you introduce more roles, and it is not impossible that the same user is assigned multiple roles, for example doctor and supervisor of a department.
I've got this model in my Django application:
class ClubSession(models.Model):
location = models.CharField(max_length=200)
coach = models.ForeignKey('auth.User', on_delete=models.CASCADE)
date = models.DateTimeField(default=now)
details = models.TextField()
def __str__(self):
return self.title
I can run python manage.py makemigrations club_sessions without issue but when I thn run python manage.py migrate club_sessions I get ValueError: Field 'id' expected a number but got 'username'. username is a superuser and already exists.
How do I resolve this?
This is the latest migration:
# Generated by Django 3.0.6 on 2020-05-28 15:07
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('club_sessions', '0004_auto_20200528_1450'),
]
operations = [
migrations.AlterField(
model_name='clubsession',
name='coach',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
),
migrations.AlterField(
model_name='clubsession',
name='location',
field=models.CharField(max_length=200),
),
]
By default Django lets a ForeignKey refer to the primary key of the target model. This also has some advantages to make relations more uniform.
If you really want to save the username in the ForeignKey, you can specify a to_field=… parameter [Django-doc] and let it refer to a column that is unique (the username of the default User model is unique), so we can refer to it with:
from django.conf import settings
class ClubSession(models.Model):
location = models.CharField(max_length=200)
coach = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
to_field='username'
)
date = models.DateTimeField(default=now)
details = models.TextField()
def __str__(self):
return self.title
You will need to remove the already existing migration and make a new one in order to migrate the database properly.
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
Is there a way to extend the built-in Django Group object to add additional attributes similar to the way you can extend a user object? With a user object, you can do the following:
class UserProfile(models.Model):
user = models.OneToOneField(User)
and add the following to the settings.py file
AUTH_PROFILE_MODULE = 'app.UserProfile'
which gets you:
profile = User.objects.get(id=1).get_profile()
Is there any equivalent to this approach for extending a group? If not, is there an alternative approach I can take?
If you simply subclass the Group object then by default it will create a new database table and the admin site won't pick up any new fields.
You need to inject new fields into the existing Group:
if not hasattr(Group, 'parent'):
field = models.ForeignKey(Group, blank=True, null=True, related_name='children')
field.contribute_to_class(Group, 'parent')
To add methods to the Group, subclass but tag the model as proxy:
class MyGroup(Group):
class Meta:
proxy = True
def myFunction(self):
return True
You can create a model that subclasses Group, add your own fields, and use a Model Manager to return any custom querysets you need. Here's a truncated example showing how I extended Group to represent Families associated with a school:
from django.contrib.auth.models import Group, User
class FamilyManager(models.Manager):
"""
Lets us do querysets limited to families that have
currently enrolled students, e.g.:
Family.has_students.all()
"""
def get_query_set(self):
return super(FamilyManager, self).get_query_set().filter(student__enrolled=True).distinct()
class Family(Group):
notes = models.TextField(blank=True)
# Two managers for this model - the first is default
# (so all families appear in the admin).
# The second is only invoked when we call
# Family.has_students.all()
objects = models.Manager()
has_students = FamilyManager()
class Meta:
verbose_name_plural = "Families"
ordering = ['name']
def __unicode__(self):
return self.name
I managed to use migrations with #Semprini aswer.
So i needed to create a company related field in my groups related field, so in my models i did this:
if not hasattr(Group, 'company'):
field = models.ForeignKey(Company, on_delete=models.DO_NOTHING, null=True)
field.contribute_to_class(Group, 'company')
class Group(Group):
class Meta:
proxy = True
Then i run manage.py makemigrations. This created 2 files. One with dependencies on the other, but the first one belonging to the auth app was created inside my virtual enviroment. The files look like this:
# Generated by Django 2.2.5 on 2019-10-08 16:00
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('myapp', '0013_guestuser_permissions_20190919_1715'),
('auth', '0011_update_proxy_permissions'),
]
operations = [
migrations.AddField(
model_name='group',
name='company',
field=models.ForeignKey(
null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='myapp.Company'),
),
]
The second one created in myapp migrations folder look like this:
# Generated by Django 2.2.5 on 2019-10-08 16:00
import django.contrib.auth.models
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('auth', '0012_group_company_20191008'),
('myapp', '0013_guestuser_permissions_20190919_1715'),
]
operations = [
migrations.CreateModel(
name='Group',
fields=[
],
options={
'proxy': True,
'indexes': [],
'constraints': [],
},
bases=('auth.group',),
managers=[
('objects', django.contrib.auth.models.GroupManager()),
],
),
]
So the solution was to move the file created in my virtualenv to myapp migrations folder, before the other one generated with makemigrations, but since the migration is applied to the auth app instead of myapp i have to implement a workaround in the file. So the final file now is:
# Generated by Django 2.2.5 on 2019-10-08 16:00
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('myapp', '0013_guestuser_permissions_20190919_1715'),
('auth', '0011_update_proxy_permissions'),
]
operations = [
migrations.AddField(
model_name='group',
name='company',
field=models.ForeignKey(
null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='myapp.Company'),
),
]
def mutate_state(self, project_state, preserve=True):
"""
This is a workaround that allows to store ``auth``
migration outside the directory it should be stored.
"""
app_label = self.app_label
self.app_label = 'auth'
state = super(Migration, self).mutate_state(project_state, preserve)
self.app_label = app_label
return state
def apply(self, project_state, schema_editor, collect_sql=False):
"""
Same workaround as described in ``mutate_state`` method.
"""
app_label = self.app_label
self.app_label = 'auth'
state = super(Migration, self).apply(project_state, schema_editor, collect_sql)
self.app_label = app_label
return state
The mutate an apply methods allow you to migrate to the auth app from myapp migrations.
In the second file i just change the dependencie to depend on the newly file created:
# Generated by Django 2.2.5 on 2019-10-08 16:00
import django.contrib.auth.models
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('myapp', '0014_group_company_20191008'),
('myapp', '0013_guestuser_permissions_20190919_1715'),
]
operations = [
migrations.CreateModel(
name='Group',
fields=[
],
options={
'proxy': True,
'indexes': [],
'constraints': [],
},
bases=('auth.group',),
managers=[
('objects', django.contrib.auth.models.GroupManager()),
],
),
]
For me worked solution based on:
https://docs.djangoproject.com/pl/1.11/topics/auth/customizing/#extending-user
Let me explain what I did with Groups extending default model with email alias:
First of all I created my own django application let name it
python manage.py startapp auth_custom
Code section:
In auth_custom/models.py I created object CustomGroup
from django.contrib.auth.models import Group
from django.db import models
class CustomGroup(models.Model):
"""
Overwrites original Django Group.
"""
def __str__(self):
return "{}".format(self.group.name)
group = models.OneToOneField('auth.Group', unique=True)
email_alias = models.EmailField(max_length=70, blank=True, default="")
In auth_custom/admin.py:
from django.contrib.auth.admin import GroupAdmin as BaseGroupAdmin
from django.contrib.auth.models import Group
class GroupInline(admin.StackedInline):
model = CustomGroup
can_delete = False
verbose_name_plural = 'custom groups'
class GroupAdmin(BaseGroupAdmin):
inlines = (GroupInline, )
# Re-register GroupAdmin
admin.site.unregister(Group)
admin.site.register(Group, GroupAdmin)
After making migrations I have such result in Django Admin view.
Custom Group in Django Admin
In order to access this custom field you must type:
from django.contrib.auth.models import Group
group = Group.objects.get(name="Admins") # example name
email_alias = group.customgroup.email_alias
If any mistakes please notify me, I'll correct this answere.