2010-09-09 9 views
13

AntecedentesAdición de permisos por objeto django administrador

Estoy desarrollando una aplicación Django para un sitio de alquiler de vacaciones. Tendrá dos tipos de usuarios, inquilinos y administradores de propiedades.

Me gustaría que los administradores de la propiedad puedan administrar sus propiedades de alquiler en el administrador de django. Sin embargo, solo deberían poder administrar sus propias propiedades.

Me doy cuenta de que el administrador de django predeterminado no es compatible con esto. Me pregunto cuántos problemas sería agregar esta funcionalidad y, si es factible, cuál es la mejor manera de manejarla.


Meta

Lo ideal es que la imagen que funcione algo como esto:

auth ya permite permisos de este tipo:

 
vacation | rental | Can add rental 
vacation | rental | Can change rental 
vacation | rental | Can delete rental 

me gustaría cambiar esto a algo como:

 
vacation | rental | Can add any rental 
vacation | rental | Can change any rental 
vacation | rental | Can delete any rental 
vacation | rental | Can add own rental 
vacation | rental | Can change own rental 
vacation | rental | Can delete own rental 

Posible solución

¿Cómo el marco decidir si el alquiler (o lo que sea) pertenece al usuario? Estoy pensando que comprueba la clase vacation.Rental para ver si tiene un ForeignKey a auth.User (posiblemente con algún nombre particular, como 'propietario').

  • En la creación de un nuevo vacation.Rental, el valor del campo ForeignKey se verían obligados a ID del usuario actual. El campo ForeignKey no se mostraría en el formulario.

  • En la lista de alquileres, solo se mostrarán los alquileres con ForeignKey que coincidan con el usuario actual.

  • En los alquileres de cambio, solo se mostrarán los alquileres con ForeignKey que coincidan con el usuario actual. El campo ForeignKey no se mostraría en el formulario.

Por supuesto, esto debe ser capaz de funcionar para cualquier modelo que tenga un campo apropiado ForeignKey, no sólo nuestro modelo vacation.Rental.

¿Esto suena factible hasta el momento, o debería ir en una dirección diferente?


complicaciones

Ahora, aquí está la parte difícil; No estoy seguro de cómo manejar esto. Digamos que un Rental puede tener muchas "RentalPhotos". RentalPhoto tiene un ForeignKey a Rental.Los usuarios deberían poder agregar fotos a sus propios alquileres. Sin embargo, las fotos no tienen un usuario ForeignKey, por lo que no hay forma de saber directamente a quién pertenece la foto.

¿Se puede resolver esto con algún truco en el marco, siguiendo a ForeignKey s hasta encontrar un objeto con un ForeignKey como usuario? ¿O debería tomar el camino más fácil y dar RentalPhoto (y todo lo demás 'perteneciente' a Rental) su propio ForeignKey al auth.User apropiado? El segundo enfoque invitaría redundancia innecesaria, el primero probablemente requeriría una sobrecarga de procesamiento innecesaria ...

Si me extravío por completo, no dude en señalarme en la dirección correcta. Gracias de antemano por cualquier ayuda.

Respuesta

22

Simplemente agregaría un método a cada modelo is_owned_by(user), y corresponde al modelo decidir si es propiedad de ese usuario o no. En la mayoría de los casos, is_owned_by puede ser una función genérica en una clase de modelo base y puede modificarla en casos especiales. p.ej.

class RentalPhoto(BaseModel): 
    def is_owned_by(self, user): 
     return self.rental.is_owned_by(user) 

Esto es lo suficientemente genérico y ser explícito tendrá un control completo de cómo se comportan las cosas.

Para agregar un nuevo permiso, puede agregarlo a sus modelos, p.

class Rental(models.Model): 
    # ... 
    class Meta: 
     permissions = (
      ("can_edit_any", "Can edit any rentals"), 
     ) 

creo que en lugar de añadir dos permiso para que any y own, debe agregar sólo own permiso, por lo que cada objeto ya tiene can_edit que se puede tratar como usuario puede editar sólo su objeto, y si el usuario tiene can_edit_any permiso que solo él tiene permiso para editar todo

Al usar esto podemos extender auth agregando un backend personalizado ej.

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 

Esta es una implemenation muy rápido, en realidad se puede extender objetos de permisos para comprobar si se necesita o no y el objeto, por ejemplo, permission.is_per_object en lugar de hacer búsqueda de cadenas de crudo, pero que también debería funcionar si tiene nombres estándar

+0

Gracias, Anurag; no es tan genérico como me gustaría pero lo consideraré. ¿Cómo puedo atar esto a 'auth' para que mis nuevos permisos aparezcan en la lista? –

+0

@no ver la edición. –

1

puede esto ser resueltos por algún truco en el marco, siguiendo ForeignKeys hasta que un objeto se encuentra con un ForeignKey a usuario?

no veo donde hay engaño necessairy: RentalPhoto -> Alquiler -> Usuario Así que para conseguir que el usuario de un RentalPhoto particular que le llame algo como esto en la instancia:

photo.rental.user 

Seguir relaciones múltiples en un solo paso se puede considerar como un truco.

+0

que trabajaría para este caso en particular, pero quiero hacerlo de un modo genérico - "Por supuesto, esto debe ser capaz de funcionar para cualquier modelo que tenga un campo 'ForeignKey' apropiado, no solo nuestro modelo' vacation.Rental' [y relacionado] [s]. " –

+0

Hay como docenas de formas posibles de ser más genérico. P.ej. tener un modelo.Ownable que define el propietario del campo y extenderlo en los Modelos propios. –

+0

Esta respuesta no es muy útil. Deja la mayor parte de la pregunta sin respuesta. Tal vez hice un mal trabajo al explicar la pregunta? –

0

Está en Django docs.

Básicamente, usted crea una clase de administración personalizada para su modelo y define el método get_queryset. En tu caso, podría ser algo como a continuación.El súper usuario vería todos los alquileres, mientras que el propietario solo el suyo.

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) 

Aquí es otra ventaja posible: https://code.djangoproject.com/wiki/RowLevelPermissions

Cuestiones relacionadas