I'm searching for a way to customize the Django Administration to support permissions and data based on the user group.
For example, I've just created the Developers1, Developers2 groups.. now I've also created the Transaction model, with AdminModel to specify how to list data.
Transacton model:
class Transaction(models.Model):
income_period_choices = (('Weekly', 'Weekly'), ('Fortnightly',
'Fortnightly'))
chp_reference = models.CharField(max_length=50, unique=True)
rent_effective_date = models.DateField(null=True, blank=True)
income_period = models.CharField(max_length=11,
choices=income_period_choices,
null=True,
blank=True)
property_market_rent = models.DecimalField(help_text='Weekly',
max_digits=7,
decimal_places=2,
null=True,
blank=True)
*group = models.ForeignKey(Group, on_delete=models.CASCADE)
im not sure about the *group field, should i delete it , or should i create Charfield, which is not a foreignkey to the django.contrib.auth.group model?
and this is the admin transaction:
#admin.register(Transaction)
class TransactionAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
obj.user = request.user
super().save_model(request, obj, form, change)
def get_queryset(self, request):
qs = super().get_queryset(request)
# for s in qs:
if request.user.is_superuser:
return qs
return qs.filter(group_name__in=Group)
search_fields = ['chp_reference','familymember__name']
inlines = [FamilyGroupInline,FamilyMemberInline]
what im trying to do is i want each group to only access its own Transaction model, and each group can add, delete, update and view their own Transactions only(eg developers1 group cant access developers2 Transactions and vice versa)
any thoughts should be appreciated
thanks!:)
To do that you can override the get_queryset method to only return the objects that are relevant to these users (based on their permissions, groups or any condition you want).
Related
I want to display Customers from specific groups in the ListView, not able to understand how to get the queryset
class CustomerList(ListView):
model = Customer
queryset = Customer.objects.filter(member__groups__name__in=['online', 'whatsapp'])
template_name = 'customer/customer_list.html'
models.py
class Customer(models.Model):
member = models.ForeignKey(User, verbose_name=_("Customer"), on_delete=models.CASCADE)
contact = models.ForeignKey(Contact, verbose_name=_("Contact"), on_delete=models.CASCADE, blank=True, null=True)
...
Customers are added to the groups as below:
class AddUser(CreateView):
def post(self, request, *args, **kwargs):
form = UserForm(request.POST)
if form.is_valid():
user = form.save(commit=False)
group, created = Group.objects.get_or_create(name='online')
user.groups.add(group)
user.save()
I have similar code working, check if this works for you -
class ProfessorsList(generic.list.ListView):
model = Staff
queryset = Staff.objects.filter(member__groups__name='teaching')
For multiple groups, you would do as: (I believe you're already doing it...)
Customer.objects.filter(member__groups__name__in=['online', ...])
If it still does not work for you try like this:
users = User.objects.filter(groups__name__in=[your_groups])
queryset = Customer.objects.filter(member__in=users)
Make sure customers have users and users are part of your_groups
I am working on an application where currently, I have
1) Staff Model is connected to User model via OneToOne Relationship and can have more than one Group.
2) Meeting model can also assigned to many Group.
3) RSVPinline is a part with MeetingAdmin as a inline form.
Here i was trying to automatically ADD all 'Staff' associated in Selected Groups in django admin form while creating Meetings.
I have tried save_model to add current user in meeting's 'creator' field.
models.py
class Group(models.Model):
name = models.CharField(max_length=200)
class Staff(models.Model):
fullname = models.CharField(max_length = 250,verbose_name = "First Name")
group = models.ManyToManyField(Group, blank=False,verbose_name = "Meeting Group") # protect error left to add
is_active = models.BooleanField(default=True)
user = models.OneToOneField(User, on_delete=models.CASCADE,null=True, blank=True,verbose_name = "Associated as User") # protect error left to add
left_date = models.DateField(null=True, blank=True,verbose_name = "Resigned Date")
class Meeting(models.Model):
title = models.CharField(_('Title'), blank=True, max_length=200)
start = models.DateTimeField(_('Start'))
group = models.ManyToManyField(Group, blank=False,verbose_name = "Meeting Group") # protect error left to add
location = models.ForeignKey(Location, blank=False,verbose_name = "Location",on_delete=models.CASCADE) # protect error left to add
class RSVP(models.Model):
meeting = models.ForeignKey(Meeting, on_delete=models.CASCADE)
responder = models.ForeignKey(User, editable=True, on_delete=models.CASCADE, null=True, blank=True,verbose_name = "Attendees", related_name='guest')
response = models.CharField(max_length = 20, choices= MEETING_RSVP, default='No response', verbose_name = "Status",null=True, blank=True)
admin.py
class RSVPInline(admin.TabularInline):
model = RSVP
extra = 0
class MeetingAdmin(admin.ModelAdmin):
form = MeetingForm
list_display = ('title', 'location', 'start','creator' )
inlines = [RSVPInline, TaskInline]
#Currently using save_model to automatically add current user as a creator
def save_model(self, request, obj, form, change):
obj.creator = request.user
super().save_model(request, obj, form, change)
My pseudo code is:
grouplist = Get group's list from submitted MeetingForm
stafflist = Staff.objects.filter(department__in =grouplist).values_list('id', flat=True).distinct()
Add to RSVPInline:
values = list(for staff in stafflist:
'responder' = staff
'meeting' = 'meeting from MeetingForm'
'response' = has a default value in model
bulk_create() RSVPInline with values
You can extend save_related() ModelAdmin method to perform additional actions after form object (Meeting) and its Inlines (RSVPs, if present in submitted form) are saved:
class MeetingAdmin(admin.ModelAdmin):
...
def save_related(self, request, form, formsets, change):
# call original method - saves Meeting and inlines
super(MeetingAdmin, self).save_related(request, form, formsets, change)
# get this form Meeting
obj = form.instance
# get Staff members of this meeting groups
# and we can exclude ones already having
# RSVP for this meeting
stafflist = Staff.objects.filter(
group__in=obj.group.all()
).exclude(
user__guest__meeting=obj
)
rsvps = list(
RSVP(responder=staff.user, meeting=obj)
for staff in stafflist
)
# calls bulk_create() under the hood
obj.rsvp_set.add(*rsvps, bulk=False)
** Few possibly useful notes:
group field may be better to be called groups as it represents ManyToMany relation and returns multiple objects
related_name represents relation from the related object back to this one so it may be more logical to use something like invites instead of guest
I have just followed a small tutorial using DRF, but I can't figure how to like my model to his user when POSTing a new object
this is my model :
# Create your models here.
class Project(models.Model):
# project title
title = models.CharField(max_length=255, null=False)
# subtitle
subtitle = models.CharField(max_length=255, null=False)
#######
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
and so my serializer
class ProjectSerializer(serializers.ModelSerializer):
class Meta:
model = Project
fields = ("id", "title", "subtitle", "user_id")
so, now in the view I have access to the current_user with this :
request.user
class ListProjectsView(generics.ListCreateAPIView):
authentication_classes = [authentication.TokenAuthentication]
queryset = Project.objects.all()
serializer_class = ProjectSerializer
def list(self, request):
queryset = Project.objects.filter(user_id=request.user.id)
serializer = ProjectSerializer(queryset, many=True)
return Response(serializer.data)
[...]
def create(self, request, pk = None):
return super(ListProjectsView, self).create(request, pk = None)
I suppose there is a way to passe the request.user is the create in order to allow my Project.user_id to be filled ?
Whatever I'm doing, I can't manage to set the user, and i get the
django.db.utils.IntegrityError: null value in column "user_id" violates not-null constraint error
Any idea?
Thanks!
Try to override with following method. Everytime PUT/PATCH/CREATE operation is performed following method is called. This is the good way to pass the current user.
def perform_create(self, serializer):
serializer.save(user = self.request.user)
class Project(models.Model):
# project title
title = models.CharField(max_length=255, null=False)
# subtitle
subtitle = models.CharField(max_length=255, null=False)
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
okay so you have FK user but try to access it with the user_id = request.user.id
Just call
queryset = Project.objects.filter(user=request.user)
queryset = Project.objects.filter (user_id=request.user.id)
if you want to match the id you should put two __
like so user__id = request.user.id but I dont see any sence making it.
I am trying to create a simple app. It has the following model:
Model: Product
class Product(models.Model):
product_owner = models.ForeignKey(User, verbose_name='User')
product_title = models.CharField(max_length=100, null=False,
verbose_name='Product title')
product_description = models.TextField(max_length=250, verbose_name='Product description')
product_qty = models.IntegerField(verbose_name='Quantity')
product_mrp = models.DecimalField(max_digits=12, decimal_places=2, verbose_name='Maximum retail price')
product_sku = models.CharField(max_length=100, null=False, unique=True, verbose_name='SKU',help_text='Enter Product Stock Keeping Unit')
product_barcode = models.CharField(max_length=100, null=False, verbose_name='Barcode')
I am using only the built in Admin App provided by django framework. And I was able to Make the Product data availaible for only the respective user by adding the following in the Admin classes.
class ProductAdmin(ImportExportModelAdmin):
exclude = ('product_owner',)
list_display = ['product_title','product_description', 'product_qty',
'product_mrp','product_sku','product_barcode']
search_fields = ['product_title', 'product_description', 'product_sku',
'product_barcode']
ordering = ['id']
list_display_links = ['product_title']
def get_queryset(self, request):
if request.user.is_superuser:
return Product.objects.all()
return Product.objects.filter(product_owner=request.user)
def save_model(self, request, obj, form, change):
if not change:
obj.product_owner = request.user
obj.save()
As I just started experimenting, I added 2 users, User1 and User2.
For User1 I added 10 products. Then I loggen in as User2 and User2 cannot see the products added by User1.
Now when I am tying to add products for User2, and if there is a conflict in product_sku field which is a unique field, I cannot add the product at all.
How to solve this. I need each user to add his own products, but product_sku unique to his set of products data, not for the whole database.
Instead of defining unique=True on the product_sku field, you should define unique_together for sku and user.
class Product(models.Model):
...
class Meta:
unique_together = (('product_sku', 'product_owner'),)
This will ensure that each user can only have one product with a particular sku, but multiple users can have the same sku.
I have a Django application, and have two apps within the project: accounts and store. In my store app, I have Product, Category model and other models.
I have admin site, from where I can register Products. In the accounts, I allow general people to signup and log in.
Now, I also want users to be able to register Products, so that they can sell their own products. However, I do not want to give them to a complete access to the admin site.
How can I give such limited access to the admin site to general users?
Thanks.
In admin you can make groups and assign access to models you wanted and same could be applied to users but you might be interested in limited access to models records to which logged user have itself added. in order to achieve this you have to define one column in model to foreign key to users like below I have defined model for company and assigned each company to user:
class Company(models.Model):
name = models.CharField(max_length=64, primary_key=True)
kam = models.ForeignKey(User, verbose_name='KAM', blank=True, null=True)
address = models.TextField(blank=True, null=True)
city = models.CharField(max_length=32, blank=True, null=True)
country = models.CharField(max_length=32, blank=True, null=True)
phone = models.CharField(max_length=32, blank=True, null=True)
fax = models.CharField(max_length=32, blank=True, null=True)
url = models.URLField(blank=True, null=True)
class Meta:
verbose_name_plural = 'Companies'
#unique_together = (('name', 'kam'),).
def __unicode__(self):
return self.name
Now your model will be associated with user, Now you could restrict records to be loaded according to logged user using admin.py in modeladmin definition like given below:
def queryset(self, request):
qs = super(CompanyAdmin, self).queryset(request)
# If super-user, show all comments
if request.user.is_superuser:
return qs
return qs.filter(kam=request.user)
thats simple let me know if this is what you want?
Also you could assign read only right. in model admin
Admin uses a class ModelAdmin to render the page as you would probably already know. That class has a queryset method which you override based with a new filter, based on who is accessing the site, as suggested by sharafjaffri.
But that filtering alone isn't sufficient. You also need to filter the values displayed in the dropdowns, to only those created by the user. And then when saved, you should associate the new object with the portal of the user adding it.
Here is my quick untested implementation of the same:
class PortalAdmin(admin.ModelAdmin):
exclude = ('portal',)
def queryset(self, request):
"""
Filter the objects displayed in the change_list to only
display those for the currently signed in user.
"""
qs = super(UserAdmin, self).queryset(request)
if request.user.is_superuser:
return qs
else:
return qs.filter(portal=request.user.profile.portal)
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
the_model = db_field.related.parent_model
if hasattr(the_model,'portal'):
kwargs['queryset'] = the_model.objects.filter(portal=request.portal)
return super(PortalAdmin,self).formfield_for_foreignkey(db_field, request, **kwargs)
def save_model(self, request, obj, form, change):
if not change:
obj.portal = request.portal
obj.save()