How to configure Tastypie to treat a field as unique?

How to configure Tastypie to treat a field as unique? My expectation would be to get some non-500 error (maybe a 409 conflict?) As an answer, if I try to insert duplicate entries for a field marked as unique.


I looked through the docs and it looks like this should be obvious to me, but for some reason I am not getting the answer that I expect to see.

Here is a link to the documentation:

http://readthedocs.org/docs/django-tastypie/en/latest/fields.html?highlight=unique


Sample code is as follows:

urls.py

v1_api = Api(api_name='v1') v1_api.register(CompanyResource()) urlpatterns = patterns('', (r'^api/', include(v1_api.urls)), ) 

resource.py

 class CompanyResource(ModelResource): CompanyName = fields.CharField(attribute='company_name') CompanyId = fields.CharField(attribute='company_id', unique=True) Contact = fields.CharField(attribute='contact') Email = fields.CharField(attribute='email') Phone = fields.CharField(attribute='phone') class Meta: queryset = Company.objects.all() authentication = BasicAuthentication() authorization = Authorization() allowed_methods = ['get', 'post'] 

models.py

 class Company(models.Model): company_name = models.TextField(default=None, blank=True, null=True) company_id = models.CharField(default='', unique=True, db_index=True, max_length=20) contact = models.TextField(default=None, blank=True, null=True) email = models.EmailField(default=None, blank=True, null=True) phone = models.TextField(default=None, blank=True, null=True) 

The error I get is the following: (using curl to get into my local service):

 curl --dump-header - -H "Content-Type: application/json" -X POST --user user:password --data '{"CompanyName": "company", "CompanyId": "1234567890", "Contact": "John", "Email": " example@example.com ", "Phone": "555-555-5555"}' http://localhost:8000/api/v1/company/ HTTP/1.0 500 INTERNAL SERVER ERROR Date: Thu, 15 Sep 2011 18:25:20 GMT Server: WSGIServer/0.1 Python/2.7.1 Content-Type: application/json; charset=utf-8 {"error_message": "(1062, \"Duplicate entry '1234567890' for key 'api_company_company_id_uniq'\")", ...<snip>... raise errorclass, errorvalue\n\nIntegrityError: (1062, \"Duplicate entry '1234567890' for key 'api_company_company_id_uniq'\")\n"} 

When I remove unique=True, db_index=True, from the company model, I do not get an Integrity error, but instead a new duplicate resource is created. Again, this is not the expected result, since I would expect a unique preliminary check of some validation and calling some answer not 500.

+7
source share
3 answers

This is how I solved the problem:

Based on the documentation for verification, I was able to implement a special validator that checked for me the uniqueness of the field. http://django-tastypie.readthedocs.org/en/latest/validation.html

In CompanyResource, I added a meta CustomValidation to the class. I put the implementation for CustomValidation in the validations.py file. If isValid returns errors, api will return 400 with messages contained in the errors.

 class CompanyResource(ModelResource): """ CompanyIds should be unique """ CompanyName = fields.CharField(attribute='company_name') CompanyId = fields.CharField(attribute='company_id', unique=True) Contact = fields.CharField(attribute='contact') Email = fields.CharField(attribute='email') Phone = fields.CharField(attribute='phone') class Meta: queryset = Company.objects.all() authentication = BasicAuthentication() authorization = Authorization() allowed_methods = ['get', 'post'] validation = CustomValidation() 

validations.py

 class CustomValidation(Validation): """ The custom validation checks two things: 1) that there is data 2) that the CompanyId exists (unique check) """ def is_valid(self, bundle, request=None): if not bundle.data: return {'__all__': 'Missing data, please include CompanyName, CompanyId, Contact, Email, and Phone.'} errors = {} company_id=bundle.data.get('CompanyId', None) # manager method, returns true if the company exists, false otherwise if Company.objects.company_exists(company_id): errors['CompanyId']='Duplicate CompanyId, CompanyId %s already exists.' % company_id return errors 
+8
source

I had the same problem today. Here's how I handle it:

Override the [request_method] _ [request_type] method in your resource definition. For example, I override post_list in FooResource:

 def post_list(self, request, **kwargs): from django.db import IntegrityError try: return super(FooResource, self).post_list(request, **kwargs) except IntegrityError, e: if e.args[0] == 1062: return http.HttpConflict() 

Hope this works for you.

+5
source

For what it's worth, I created a slightly different solution that works better for me. This is based on the thoslin answer.

I found that checking e.args [0] == 1062 is not suitable for me. I am sure this is a MySQL error and I am using Postgres.

I also implemented this in the obj_create method so that it handles all the creation of the object, and not only this was done through post_list.

 from tastypie.exceptions import ImmediateHttpResponse from tastypie.http import HttpConflict from django.db import IntegrityError ... class MyBaseResource(ModelResource): def obj_create(self, bundle, **kwargs): try: return super(MyBaseResource, self).obj_create(bundle, **kwargs) except IntegrityError, e: if e.args[0] == 1062 or e.args[0].startswith('duplicate key'): raise ImmediateHttpResponse(HttpConflict()) ... class MyResource(MyBaseResource): [usual resource stuff] 
0
source

All Articles