When i’m creating a new resource with a foreign relation, specified as, i.e., {"pk": 20}, i get a new unwanted FK-item created.
I have Order model class with a relations to the Language model, so when creating an Order instance, i may have to specify the order’s languages. Language list should be constant, and the users must not have an ability to modify existant or to create the new languages.
Order resource:
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()
And here is a Language resource:
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'])
I’m trying to create a new Order with 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"
});
Instead of setting the pk:20 to src_lang_id field, it creates a new Language with empty fields for src_lang and sets a correct value for dst_lang. But empty fields are restricted with the Language model definition. How it saves it?
Also it’s enough strange because i’ve straightly specified readonly access for language model, and only get method for accessing the supported language list.
If i declare language fields of OrderResource class as, i.e.: src_lang = fields.ForeignKey(LanguageResource, 'src_lang', null=True, full=True, readonly=True), it creates nothing, but also does not set any values for the foreign keys.
So, i just need to specify an existant language, i don’t need to create it.
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()
UPDATE 2
I found nothing more clever making fields read only and overriding obj_create method:
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()
As outlined in this answer to your question,
src_langshould correspond to a resource, not to some other value. I suspect that when the POST occurs and it doesn’t find a resourcepk=20, it creates a newLanguageobject and callssavewithout Django model validation, allowing a blank field to exist in the createdLanguage.One way of forcing a read-only type resource is to create a resource which doesn’t allow
obj_create.This resource is then referenced from your
Orderresource, overriding just thesrc_langfield to point to your read only resource.Any request that references an existing resource will complete as per normal (but you’ll need to reference the resource correctly, not using
pk=20). Any request the references an unknown language will fail as a newLanguageobject cannot be created.