Как настроить Tastypie для обработки поля как уникального?

Как настроить Tastypie для обработки поля как уникального? Мое ожидание состояло бы в том, чтобы получить какую-то ошибку, отличную от 500 (возможно, конфликт 409?) В качестве ответа, если я попытаюсь вставить повторяющиеся записи для поля, помеченного как уникальное.


Я просмотрел документы, и похоже, что это должно быть очевидно для меня, но по какой-то причине я не получаю ответа, который ожидал бы увидеть.

Вот ссылка на документацию:

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


Пример кода выглядит следующим образом:

urls.py

v1_api = Api(api_name='v1')
v1_api.register(CompanyResource())

urlpatterns = patterns('',
    (r'^api/', include(v1_api.urls)),
)

ресурс.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']

модели.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)

Ошибка, которую я получаю, следующая (используя curl для доступа к моей локальной службе):

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

Когда я удаляю unique=True, db_index=True, из модели Company, я не получаю сообщение об ошибке Integrity, вместо этого создается новый повторяющийся ресурс. Опять же, это не ожидаемый результат, поскольку я ожидаю, что уникальный выполнит некоторую проверку и вызовет ответ, отличный от 500.


person johncosta    schedule 15.09.2011    source источник
comment
Я сталкиваюсь с тем же самым с полями модели, которые определены как пустые = True. Tastypie создает записи с пустыми записями.   -  person Erik    schedule 09.07.2012
comment
Ответ для меня состоял в том, чтобы использовать validation=FormValidation(form_class=‹MyModelForm›). Это проверяет поля вашей модели.   -  person Erik    schedule 09.07.2012


Ответы (3)


Вот как я решил проблему:

Основываясь на документации по валидации, я смог реализовать кастомный валидатор, который проверял для меня уникальность поля. http://django-tastypie.readthedocs.org/en/latest/validation.html

В CompanyResource я добавил к метаклассу CustomValidation. Я поместил реализацию CustomValidation в файл validations.py. Если isValid возвращает ошибки, API вернет 400 с сообщениями, включенными в ошибки.

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()

валидации.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
person johncosta    schedule 21.09.2011
comment
Это не так. Если вам нужна поддержка PUT, вам нужно будет использовать подход «thoslins», описанный выше. - person astevanovic; 31.08.2012

У меня была такая же проблема сегодня. Вот как я с этим справляюсь:

Переопределите метод [request_method]_[request_type] в определении вашего ресурса. Например, я переопределяю post_list в 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()

Надеюсь, это сработает для вас.

person thoslin    schedule 09.01.2012
comment
Почему вы решили импортировать IntegrityError внутри метода? - person Jake; 09.04.2014

Что бы это ни стоило, я создал немного другое решение, которое работает лучше для меня. Это основано на ответе Тослина.

Я обнаружил, что проверка e.args[0] == 1062 не работает для меня. Я почти уверен, что это ошибка MySQL, и я использую Postgres.

Я также реализовал это в методе obj_create, чтобы он обрабатывал создание всех объектов, а не только то, что делается через 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]
person Jake    schedule 09.04.2014