I have a simple model, say Resources. And I have fewer than 20 users and model admin serves the purpose to record requests.
Problem is that all users can see all records in model admin site.
Can this behaviour be changed to only show records created by same user only ?
Thank you in anticipation.
The django doc has an example that does almost exactly what you want:
https://docs.djangoproject.com/en/1.11/ref/contrib/admin/#django.contrib.admin.ModelAdmin.get_queryset
The idea is to override the get_queryset() method in the model admin view:
# admin.py
from django.contrib import admin
class YourModelAdmin(admin.ModelAdmin):
def get_queryset(self, request):
qs = super(MyModelAdmin, self).get_queryset(request)
return qs.filter(author=request.user)
admin.site.register(YourModel, YourModelAdmin)
You can adapt the queryset filter to even more specific needs at will.
UPDATE 2020:
Anyone who is curious as to what is the author field, then that is established in the models.py file of your app. For the admin.py part, you can visit the docs.
Step 1:
Make sure in your permissions you give access to the apps you want your users to have CRUD functionality over.
Step 2:
Admin.py
class MyModelAdmin(admin.ModelAdmin):
def get_queryset(self, request):
qs = super().get_queryset(request)
if request.user.is_superuser:
return qs
return qs.filter(author=request.user)
Models.py
from django.contrib.auth import get_user_model
class Lecture(models.Model):
author = models.ForeignKey(get_user_model(), null=True, on_delete=models.CASCADE)
Related
So, I am really new to Django. I was working around an app as a part of an assignment which requires me to build a webapp with login functionality, Further the admin, shall create few tasks that will be common to all the users. So here is the model that would contain all the tasks and will be controlled by the admin:
from django.db import models
# Create your models here.
class applib(models.Model):
status_code=[
('C','Completed'),
('P','Pending'),
]
title = models.CharField(max_length=50)
status=models.CharField(max_length=2,choices=status_code)
point = models.IntegerField()
category= models.CharField(max_length=50)
subcat = models.CharField(max_length=50)
applink = models.CharField(max_length=100)
def __str__(self):
return self.title
Now I have worked around the login functionality, and I want that upon each login a copy of this model is attached to the user so that the user can has his own set of tasks. He should be able to keep track of these tasks and do the tasks independently. How do I do this without creating separate task models for each user. I know there would be a really simple explanation and solution but all I know write now is I want to inherit the tasks from the main model for each user upon user creation.
Thank you.
You need to add a user field to you AppLib (change classname to CamelCase).
class AppLib(models.Model):
...
user = models.ForeignKey(settings.AUTH_USER_MODEL, models.CASCADE, related_name="tasks")
...
This way, in the admin panel, you can assign tasks to a specific user
if you want to assign a task to a set of users:
from django.contrib.auth import get_user_model
...
class AppLib(models.Model):
...
users = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True, related_name="tasks")
...
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
# only fetch users that are active
users = list(get_user_model()._default_manager.filter(
is_active=True).values_list("pk", flat=True))
self.users.add(*users)
in admin.py:
from django.contrib.auth import get_user_model
from django.contrib import admin
from <model_module> import AppLib
#admin.register(AppLib)
class AppLib(admin.ModelAdmin):
...
def save_related(self, request, form, formsets, change):
super().save_related(request, form, formsets, change)
users = list(get_user_model()._default_manager.filter(
is_active=True).values_list("pk", flat=True))
form.instance.users.add(*users)
Read More about this logic here
I'm using Django-Rest-Framework(ViewSet approach) on my project interacting with a React app. So, I'm not using Django admin nor Django forms.
My project's structure is:
View
Serializer
Model
What I need to do is to perform actions before models method calls:
Insert the request.user on a Model field.
Start a printer process after a Model.save()
.....
I have read a lot about django-way to do on Django.docs and there, the things seems to be showed for a Django-Admin like project, which is not my case.
By other hand, by reading the Stack's answers about in other topics, the way to do seems to be like: "It will work, but, It's not the right way to do that".
According to Django's documentation, the best way to perform that supposed to be by using a new file, called admin.py, where I would to register actions binding to a Model which could support save, delete, etc., but, it's not clear if this approach is to do that or only for provide a Django-Admin way to perform an action.
# app/models.py
from django.db import models
from django.contrib.auth.models import User
class Post(models.Model):
user = models.ForeignKey(User)
content = models.TextField()
class Comment(models.Model):
post = models.ForeignKey(Post)
user = models.ForeignKey(User)
content = models.TextField()
# app/admin.py
from app.models import Post, Comment
from django.contrib import admin
class CommentInline(admin.TabularInline):
model = Comment
fields = ('content',)
class PostAdmin(admin.ModelAdmin):
fields= ('content',)
inlines = [CommentInline]
def save_model(self, request, obj, form, change):
obj.user = request.user
obj.save()
def save_formset(self, request, form, formset, change):
if formset.model == Comment:
instances = formset.save(commit=False)
for instance in instances:
instance.user = request.user
instance.save()
else:
formset.save()
admin.site.register(Post, PostAdmin)
According to the answers I have heard, the best way would use something like that on Models:
class MyModelForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
return super(MyModelForm, self).__init__(*args, **kwargs)
def save(self, *args, **kwargs):
kwargs['commit']=False
obj = super(MyModelForm, self).save(*args, **kwargs)
if self.request:
obj.user = self.request.user
obj.save()
return obj
What I want to know is:
What's the best way to to perform that actions, on which files, what's the best structure.
to insert a request.user on a Model field you can use the perform_create() method on your view class. for more information visit https://www.django-rest-framework.org/tutorial/4-authentication-and-permissions/#associating-snippets-with-users which is exactly what u want!
I'm not sure what you mean by start a printer process, but you usually can override save() method on your model class for doing a process after saving a model instace.
https://www.django-rest-framework.org/api-guide/serializers/#overriding-save-directly
The best way I found to insert the request.user on the model, as a "created_by" field, was by inserting a hidden field on the model serializer with a default data, just like these:
my_field = serializers.HiddenField(default=serializers.CurrentUserDefault())
The CurrentUserDefault() is a function wich returns the user request onto serializer.
https://www.django-rest-framework.org/api-guide/validators/#advanced-field-defaults
For actions performing after/before a save/delete, what I chose to use Django Signals,wich works as a dispatcher of actions, a little like react's redux.
https://docs.djangoproject.com/en/2.2/topics/signals/
Thank you everybody for the helpful answers.
I tried overwriting def has_delete_permission(self, request, obj=None):, but obj is not passed when deleting a record in Django Admin.
Any help would be appreciated! Thank you.
model
from django.contrib.auth.models import User
class Item(models.Model):
owner = models.ForeignKey(User, on_delete=models.CASCADE)
info = models.TextField()
view
obj = Item.objects.get(id=1)
if obj.owner == request.user:
obj.delete()
In order to do this within Django Admin you need to override the delete_model method in admin.py file. Use the following code for the model above to make sure the users only delete their own content, and no one else's.
admin.py
class ItemAdmin(admin.ModelAdmin):
def delete_model(self, request, obj):
if obj.owner == request.user:
obj.delete()
return
admin.site.register(Item, ItemAdmin)
Now try to create two objects in the model from the admin page and use two separate users as the owner. You will be able to delete only your content, but not others. I checked the code. This works for me.
I am using the django admin site for my web app, but i am having a problem. I need that the staff users can change, create and delete another staff users, but i don't want that they change the informations of the superusers. I want to know if is possible filter the user list by role (the staff user don't see the superusers in the list).
Finally I found how to do this, I leave the code here just in case someone hav the same problem that I had
def get_queryset(self, request):
queryset = super(UserAdmin, self).get_queryset(request)
if request.user.is_superuser:
return queryset
return queryset.filter(is_superuser=False)
You will need to create a custom ModelAdmin for the User model. I recommend you to inherit from the original one and then you can override the get_queryset method.
You should end with:
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
class MyUserAdmin(UserAdmin):
def get_queryset(self, request):
qs = super(MyUserAdmin, self).get_queryset(request)
if request.user.is_superuser:
return qs
else:
return qs.filter(is_superuser=False)
admin.site.unregister(User)
admin.site.register(User, MyUserAdmin)
I have a question regarding permissions. I would like to separate my Users who are marked as Staff ( and can enter the administration page) through countries. So for example an admin from Netherlands can only see and edit the users from netherlands. What is the best approach to do that? Should I make the querys in a way so users can only see users from their own country and than I can customize it with permissions?
my models.py defines a country
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='profile', unique=True)
country = models.CharField("Country", max_length=150, blank=False)
and I thought about something like this in admin.py
def queryset(self, request):
qs = super(ProfileAdmin, self).queryset(request)
if request.user.is_superuser:
return qs
return qs.filter(country=request.user.country)
You are thinking in the right direction. It is the proper way to filter objects visible to admins.
Small corrections: the name of the method is get_queryset() and to access the user's country you should use the profile relation:
def get_queryset(self, request):
qs = super(ProfileAdmin, self).get_queryset(request)
if request.user.is_superuser:
return qs
return qs.filter(country=request.user.profile.country)
UPDATE: To filter users in the standard User admin you have to replace the UserAdmin with your own subclass:
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
class MyUserAdmin(UserAdmin):
def get_queryset(self, request):
qs = super(MyUserAdmin, self).get_queryset(request)
if request.user.is_superuser:
return qs
return qs.filter(profile__country=request.user.profile.country)
admin.site.unregister(User)
admin.site.register(User, MyUserAdmin)