23👍
I would simply add a method to each model is_owned_by(user)
, and it is upto the model to decide if it is owned by that user or not. In most case is_owned_by
can be a generic function in a base model class and you can tweak it in special cases. e.g.
class RentalPhoto(BaseModel):
def is_owned_by(self, user):
return self.rental.is_owned_by(user)
This is generic enough and being explicit you will have full control how things behave.
To add new permission you can add that to your models e.g.
class Rental(models.Model):
# ...
class Meta:
permissions = (
("can_edit_any", "Can edit any rentals"),
)
I think instead of adding two permission for any
and own
, you should add only own
permission , so each object already has can_edit
which you can treat as user can edit only his object, and if user has permission can_edit_any than only he is allowed to edit all
Using this we can extend auth by adding a custom backend e.g.
class PerObjectBackend(ModelBackend):
def has_perm(self, user_obj, perm, obj=None):
allowed = ModelBackend.has_perm(self, user_obj, perm)
if perm.find('any') >=0 :
return allowed
if perm.find('edit') >=0 or perm.find('delete') >=0:
if obj is None:
raise Exception("Perm '%s' needs an object"%perm)
if not obj.is_owned_by(user_obj):
return False
return allowed
This is a very quick implemenation, in reality you can extend permission objects to check if it needs and object or not e.g. permission.is_per_object
instead of doing crude string search but that should also work if you have standard names
2👍
If you don’t want to implement your own Permission Backend, I recommend you to use https://github.com/chrisglass/django-rulez You will do what you want in a much easier way.
- Selenium: Element not clickable … Other Element Would Receive Click
- How does Django serve media files?
- TypeError: ‘DoesNotExist’ object is not callable
- How do I call a model method in django ModelAdmin fieldsets?
1👍
It’s in Django docs.
Basically you create custom admin class for your model, and define the method get_queryset
. In your case it could be something like below. Super user would see all rentals, while owner only his.
class RentalAdmin(admin.ModelAdmin):
def get_queryset(self, request):
qs = super(RentalAdmin, self).get_queryset(request)
if request.user.is_superuser:
return qs
return qs.filter(owner=request.user)
Here’s another possible lead: https://code.djangoproject.com/wiki/RowLevelPermissions
- Django + Postgres: A string literal cannot contain NUL (0x00) characters
- Update Django session variable in javascript?
- How to upload and read csv file in django using csv.DictReader?
- Static files won't load when out of debug in Django
0👍
Can this be solved by some trickery in
the framework, following ForeignKeys
until an object is found with a
ForeignKey to user?
I don’t see where there is trickery necessairy:
RentalPhoto -> Rental -> User
So to get the User for a particular RentalPhoto you would call something like this in the instance:
photo.rental.user
Following multiple relations in one step can be considered as non-trickery.
0👍
class Rental(models.Model):
owner: User = models.ForeignKey(
User, verbose_name='owner', related_name='rentals',
on_delete=models.CASCADE, blank=True, null=False
)
# owner_id automatically gets created by Django. Optionally annotate to help your IDE
owner_id: int
def is_owned_by(self, user: User):
# You can use self.owner == user, or self.owner.id == user.id.
# But this way owner data won't be fetched from the database
if self.owner_id == user.id:
return True
return False
class RentalPhoto(models.Model):
rental: Rental = models.ForeignKey(
Rental, on_delete=models.CASCADE, related_name='rental_photos',
blank=False, null=False,
)
def is_owned_by(self, user: User):
return self.rental.is_owned_by(user)
class RentalPhotoAdminInline(admin.StackedInline):
model = RentalPhoto
extra = 1
@admin.register(Rental)
class RentalAdmin(admin.ModelAdmin):
inlines = (RentalPhotoAdminInline,)
# staff members can only view/operate their rentals
def get_queryset(self, request):
queryset = super().get_queryset(request)
if not request.user.is_superuser:
queryset = queryset.filter(owner_id=request.user.id)
return queryset
def save_model(self, request, obj: Rental, form, change):
# set rental owner on admin save
if obj.owner_id is None:
obj.owner = request.user
obj.save()
def has_view_or_change_permission(self, request, obj=None):
allowed = super().has_view_or_change_permission(request, obj)
if obj is None:
return allowed
return request.user.is_superuser or obj.is_owned_by(request.user)
def has_view_permission(self, request, obj=None):
allowed = super().has_view_permission(request, obj)
if obj is None:
return allowed
return request.user.is_superuser or obj.is_owned_by(request.user)
def has_change_permission(self, request, obj=None):
allowed = super().has_change_permission(request, obj)
if obj is None:
return allowed
return request.user.is_superuser or obj.is_owned_by(request.user)
def has_delete_permission(self, request, obj=None):
allowed = super().has_delete_permission(request, obj)
if obj is None:
return allowed
return request.user.is_superuser or obj.is_owned_by(request.user)
- Django: serving ADMIN media files
- How to use Pagination in a Non-Generic View/Viewset?
- Write a wrapper to expose existing REST APIs as SOAP web services?
- GeoDjango: How to create a circle based on point and radius
- Django filter queryset if a field exists