2011-09-15 10 views
7

Come configurare Tastypie per trattare un campo come univoco? La mia aspettativa sarebbe quella di ricevere una sorta di errore non-500 (forse un conflitto 409?) Come risposta se provo a inserire voci duplicate per il campo contrassegnato come univoco.Come configurare Tastypie per trattare un campo come univoco?


Ho guardato attraverso i documenti e sembra che dovrebbe essere ovvio per me, ma per qualche motivo non sto ricevendo la risposta mi sarei aspettato di vedere.

ecco il link documentazione:

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


Il codice di esempio è la seguente:

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) 

L'errore che ricevo è il seguente (usando curl per colpire il mio servizio locale):

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"} 

Quando rimuovo unique=True, db_index=True, dal modello azienda , Non ricevo l'errore di integrità, ma invece viene creata una nuova risorsa duplicata. Ancora una volta, questo non è il risultato atteso, poiché mi aspetterei che sia univoco effettuare una validazione e causare una risposta non 500.

+0

Sono di fronte alla stessa cosa con i campi del modello definiti come vuoto = True. Tastypie sta creando record con voci vuote. – Erik

+0

La risposta per me era usare validation = FormValidation (form_class = ). Questo vale contro i campi del tuo modello. – Erik

risposta

8

Ecco come ho risolto il problema:

Sulla base della documentazione per la convalida, sono stato in grado di implementare un validatore personalizzato che verifica l'unicità del campo per me. http://django-tastypie.readthedocs.org/en/latest/validation.html

In CompanyResource, ho aggiunto alla classe meta una CustomValidation. Ho inserito l'implementazione per CustomValidation in un file validations.py. Se isValid restituisce errori, l'API restituirà un 400 con i messaggi inclusi negli errori.

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

Come funziona con PUT? – toucan

+0

Non è così. Se hai bisogno del supporto PUT, devi seguire l'approccio 'Thoslins' qui sopra. – astevanovic

5

Ho avuto lo stesso problema di oggi. Ecco come ho a che fare:

Sovrascrivi il metodo [request_method] _ [request_type] nella tua definizione di risorsa. Ad esempio, sostituisco 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() 

Spero che funzioni per voi.

+0

Perché hai scelto di importare IntegrityError all'interno del metodo? – Jake

0

Per quello che vale, ho creato una soluzione leggermente diversa che funziona meglio per me. È basato sulla risposta di Thoslin.

Ho trovato che il controllo e.args [0] == 1062 non riesce per me.Sono abbastanza sicuro che si tratta di un errore MySQL e sto usando Postgres.

Ho implementato anche questo nel metodo obj_create in modo che gestisca tutta la creazione di oggetti, non solo quella eseguita tramite 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] 
Problemi correlati