2013-01-20 84 views
1

当我创建与外国关系的新资源,指定为,即{"pk": 20},我得到创建了一个新的不必要的FK-项目。tastypie - 禁用嵌套的对象创建

我有Order模型类与Language模型的关系,所以当创建一个Order实例时,我可能必须指定订单的语言。语言列表应该是恒定的,并且用户不能有修改存在或创建新语言的能力。

Order资源:

class OrderResource(ModelResource): 
    user = fields.ForeignKey(UserResource, 'user', null=True, full=True) 
    src_lang = fields.ForeignKey(LanguageResource, 'src_lang', null=True, full=True) 
    dst_lang = fields.ForeignKey(LanguageResource, 'dst_lang', null=True, full=True) 

    def obj_create(self, bundle, request=None, **kwargs): 
     return super(OrderResource, self).obj_create(bundle, request, user=request.user) 

    class Meta: 
     resource_name = 'orders' 
     queryset = Order.objects.all() 
     serializer = Serializer(['json']) 

     authentication = MultiAuthentication(SessionAuthentication(), ApiKeyAuthentication()) 
     authorization = ResourceAuthorization() 

这里是一个Language资源:

class Language(models.Model): 
    name = models.CharField(max_length=100) 
    code = models.CharField(max_length=100) 


class LanguageResource(ModelResource): 
    class Meta: 
     resource_name = 'languages' 
     queryset = Language.objects.all() 
     allowed_methods = ['get'] 
     authorization = ReadOnlyAuthorization() 
     serializer = Serializer(['json']) 

我试图创建一个新的Order使用jQuery:

var data = JSON.stringify({ 
    "comment": "Something random", 
    "src_lang": {"pk": "20"}, 
    "dst_lang": "/api/v2/languages/72/" 
}); 

$.ajax({ 
    type: 'POST', 
    url: '/api/v2/orders/', 
    data: data, 
    dataType: "json", 
    contentType: "application/json" 
}); 

而不是设置pk:20src_lang_id d,它会创建一个新的Languagesrc_lang空字段,并为dst_lang正确的值。但空的字段受限于Language模型定义。它如何保存它?

还因为我已经直线指定语言模型只读访问,只get用于访问支持的语言列表方法是自叹不如。

如果我将OrderResource类的语言字段声明为src_lang = fields.ForeignKey(LanguageResource, 'src_lang', null=True, full=True, readonly=True),它不会创建任何内容,但也不会为外键设置任何值。

所以,我只需要指定一个existant语言,我不需要创建它。

UPDATE

ResourceAuthorization

class ResourceAuthorization(Authorization): 
    def is_authorized(self, request, object=None): 
     user = getattr(request, 'user', None) 
     if not user: 
      return False 

     return user.is_authenticated() 

    def apply_limits(self, request, object_list): 
     if request and hasattr(request, 'user'): 
      if request.user.is_superuser: 
       return object_list 

      return object_list.filter(user=request.user) 

     return object_list.none() 

更新2

我什么也没找到更聪明的决策领域只读和压倒一切的obj_create方法:

class OrderResource(ModelResource): 
    user = fields.ForeignKey(UserResource, 'user', null=True, full=True) 
    src_lang = fields.ForeignKey(LanguageResource, 'src_lang', null=True, full=True, blank=True, readonly=True) 
    dst_lang = fields.ForeignKey(LanguageResource, 'dst_lang', null=True, full=True, blank=True, readonly=True) 

    def obj_create(self, bundle, request=None, **kwargs): 
     src_lang_id, dst_lang_id = bundle.data.get('src_lang', None), bundle.data.get('dst_lang', None) 

     if not all([src_lang_id, dst_lang_id]): 
      raise BadRequest('You should specify both source and destination language codes') 

     src_lang, dst_lang = Language.objects.guess(src_lang_id), Language.objects.guess(dst_lang_id) 
     if not all([src_lang, dst_lang]): 
      raise BadRequest('You should specify both source and destination language codes') 

     return super(OrderResource, self).obj_create(
      bundle, request, user=request.user, src_lang=src_lang, dst_lang=dst_lang 
     ) 

    class Meta: 
     resource_name = 'orders' 
     queryset = Order.objects.all() 
     serializer = Serializer(['json']) 

     authentication = MultiAuthentication(SessionAuthentication(), ApiKeyAuthentication()) 
     authorization = ResourceAuthorization() 

回答

2

正如this回答你的问题,src_lang应该对应于资源,而不是一些其他的价值。我怀疑当POST发生并且没有找到资源pk=20时,它会创建一个新的Language对象,并且在没有Django模型验证的情况下调用save,从而允许在创建的Language中存在空白字段。

强制只读型资源的一种方法是创建一个不允许使用obj_create的资源。

class ReadOnlyLanguageResource(ModelResource): 
    # All the meta stuff here. 
    def obj_create(self): 
     # This should probably raise some kind of http error exception relating 
     # to permission denied rather than Exception. 
     raise Exception("Permission denied, cannot create new language resource") 

此资源然后从Order资源引用,覆盖只是src_lang字段设置为指向您的只读资源。

class OrderResource(ModelResource): 
    user = fields.ForeignKey(UserResource, 'user', null=True, full=True) 
    src_lang = fields.ForeignKey(ReadOnlyLanguageResource, 'src_lang') 
    dst_lang = fields.ForeignKey(ReadOnlyLanguageResource, 'dst_lang') 

引用的现有资源将完成按正常(但你需要正确地引用资源,不使用pk=20)的任何请求。任何请求引用未知的语言将失败,因为无法创建新的对象Language

1

您应该指定src_lang在格式/api/v2/languages/72/对应pk=20

其次究竟是什么ResourceAuthorizationdocumentation列出了ReadOnlyAuthorization这可能对您有用。

而且授权应用的资源,而不是底层模型。当为fk创建新对象时,它不使用REST Api,但使用django.db.models及其权限。所以授权可能不适用于通过外键约束。

+0

我看到它应该指定的格式,我不明白为什么它会创建一个新的'Language'实例。如果我将使用'/ api/v2/languages/20 /'作为src_lang,它就可以正常工作。 'ResourceAuthorization'是[每用户授权](http://django-tastypie.readthedocs.org/en/latest/cookbook.html#creating-per-user-resources) –

+0

我现在看到ResourceAuthorization,但我是不确定其正确性。 –