У меня возникли проблемы с внедрением авторизации на уровне записи с tastypie 0.9.12+.Django Tastypie Рекордный уровень авторизации
Моя установка выглядит примерно так:
Модель
class UserProfile(models.Model):
def __unicode__(self):
return self.user.get_full_name()
user = models.OneToOneField(User)
class Sample(models.Model):
def __unicode__(self):
return '%s' % self.id
OPEN = 0
CLAIMED = 1
CLOSED = 2
MANUAL = 3
MODIFIED = 4
DELETED = 5
ERROR = 6
RESERVED = 7
STATUS_CHOICES = (
(OPEN, 'Open'),
(CLAIMED, 'Claimed'),
(CLOSED, 'Closed'),
(MANUAL, 'Manual'),
(MODIFIED, 'Modified'),
(DELETED, 'Deleted'),
(ERROR, 'Error'),
(RESERVED, 'Reserved'),
)
status = models.SmallIntegerField(max_length = 1, default = OPEN, choices = STATUS_CHOICES)
user_profile = models.ForeignKey(UserProfile, blank = True, null = True)
Ресурс
class BaseResource(ModelResource):
# Base class with rather strict default settings
# All other Resources extend this and override any defaults to higher permissions
class Meta:
authentication = DjangoAuthentication()
authorization = ReadOnlyAuthorization()
allowed_methods = []
class SampleResource(BaseResource): # BaseResource defines a default Meta, setting allowed_methods and authentication for all other resources in the API
UserProfile = fields.ForeignKey(UserProfileResource, 'user_profile', null = True, full = True)
class Meta(BaseResource.Meta):
queryset = Sample.objects.all()
resource_name = 'sample'
allowed_methods = ['get', 'post', 'put', 'patch']
authorization = SampleAuthorization()
always_return_data = True
def dehydrate_status(self, bundle):
return Sample.STATUS_CHOICES[bundle.data['status']][1]
def hydrate_status(self, bundle):
bundle.data['status'] = Sample.__dict__[bundle.data['status'].upper()]
return bundle
Авторизация
class SampleAuthorization(Authorization):
# Checks that the records' owner is either None or the logged in user
def authorize_user(self, bundle):
return bundle.obj.user_profile in (None, self.user_profile(bundle))
def user_profile(self, bundle):
return user_profile.objects.get(user = bundle.request.user)
def read_list(self, object_list, bundle):
print 'Read List'
return object_list.filter(Q(user_profile = self.user_profile(bundle)) | Q(user_profile = None))
def read_detail(self, object_list, bundle):
print 'Read Detail'
return self.authorize_user(bundle)
def create_list(self, object_list, bundle):
return object_list
def create_detail(self, object_list, bundle):
return self.authorize_user(bundle)
def update_list(self, object_list, bundle):
print 'Update List'
allowed = []
for obj in object_list:
if obj.user_profile in (None, self.user_profile(bundle)):
allowed.append(obj)
return allowed
def update_detail(self, object_list, bundle):
print 'Update Detail'
print bundle.obj.status, bundle.data['status']
# Compare status stored on the server against the user-set status
# If server status is >= user status
# Raise Unauthorized
if bundle.obj.status >= bundle.data['status']:
raise Unauthorized('New status must be higher than current status')
return self.authorize_user(bundle)
def delete_list(self, object_list, bundle):
raise Unauthorized('Deletion not allowed through API')
def delete_detail(self, object_list, bundle):
raise Unauthorized('Deletion not allowed through API')
Моя проблема заключается в том, что, как представляется, update_detail вызывается дважды, с разными входами. Запрошенное обновление пытается изменить статус записи, хранящейся на сервере. Новый статус должен быть выше, чем сохраненный статус, или изменение является Несанкционированным.
При выполнении приведенного выше кода, мой результат выглядит следующим образом:
Read Detail
Update Detail
0 Claimed
Update Detail
1 1
[27/Mar/2013 09:35:23] "PATCH /api/1.0/sample/1/ HTTP/1.1" 401 0
На первом проходе, то bundle.obj.status имеет правильное значение, но bundle.data [ «статус»] HAS Не было гидратировано. На втором проходе, bundle.obj.status был изменен на новый статус, а новый статус был гидратирован.
Поскольку состояние не было увлажнено на первом проходе, я не могу их достоверно сравнивать и не хочу вручную называть hydrate_status, поскольку он испортил весь процесс гидратации, выполняемый в фоновом режиме. Поскольку значения на втором проходе одинаковы, независимо от того, какой статус я ему устанавливаю, он всегда вызывает исключение Unauthorized.
Как я могу реализовать авторизацию на уровне записи, если метод дважды вызывается Tastypie с разными входами как для сохраненных, так и для новых значений статуса?