2011-09-15 14 views
7

¿Cómo configuro Tastypie para tratar un campo como único? Mi expectativa sería recibir algún tipo de error que no sea 500 (¿posiblemente un conflicto 409?) Como respuesta si trato de insertar entradas duplicadas para el campo marcado como único.¿Cómo configuro Tastypie para tratar un campo como único?


He buscado a través de los documentos y parece que debería ser obvio para mí, pero por alguna razón no estoy recibiendo la respuesta que se puede esperar para ver.

Aquí está el enlace de documentación:

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


El código de ejemplo es el siguiente:

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) 

El error que recibo es la siguiente (utilizando rizo para golpear mi servicio local):

curl --dump-header - -H "Content-Type: application/json" -X POST --user user:password --data '{"CompanyName": "company", "CompanyId": "1234567890", "Contact": "John", "Email": "[email protected]", "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"} 

Cuando quito unique=True, db_index=True, a partir del modelo de empresa , No recibo el error de integridad, sino que se crea un nuevo recurso duplicado. Nuevamente, este no es el resultado esperado, ya que espero que sea único para realizar alguna validación y causar alguna respuesta que no sea 500.

+0

Me enfrento a lo mismo con los campos del modelo que se definen como en blanco = Verdadero. Tastypie está creando registros con entradas en blanco. – Erik

+0

La respuesta para mí fue usar la validación = FormValidation (form_class = ). Esto valida contra los campos de su modelo. – Erik

Respuesta

8

Así es como he resuelto el problema:

Sobre la base de la documentación para la validación, yo era capaz de implementar un validador personalizado que comprueba la singularidad del campo para mí. http://django-tastypie.readthedocs.org/en/latest/validation.html

En CompanyResource, agregué a la clase meta a CustomValidation. Coloqué la implementación para CustomValidation en un archivo validations.py. Si isValid devuelve errores, la API devolverá un 400 con los mensajes incluidos en los errores.

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 
+0

¿Cómo funciona esto con PUT? – toucan

+0

No es así. Si necesita soporte de PUT, deberá ir al enfoque 'thoslins' anterior. – astevanovic

5

que tenían el mismo problema hoy en día. He aquí cómo lo trato:

Reemplaza el método [request_method] _ [request_type] en la definición de su recurso. Por ejemplo, puedo reemplazar post_list en 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() 

espero que funcione para usted.

+0

¿Por qué eligió importar IntegrityError dentro del método? – Jake

0

Por lo que vale, he creado una solución ligeramente diferente que funciona mejor para mí. Está basado en la respuesta de Thoslin.

Encontré que la comprobación e.args [0] == 1062 falla para mí.Estoy bastante seguro de que este es un error de MySQL y estoy usando Postgres.

También implementé esto en el método obj_create para que maneje toda la creación de objetos, no solo eso a través de 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] 
Cuestiones relacionadas