Tastypie关系字段超过2个模型

时间:2015-03-30 14:45:24

标签: python django python-3.x tastypie

我正在学习使用tastypie为Django项目编写API。我在models.py中有以下代码:

from django.db import models
from django.contrib.auth.models import User

class UserDeviceIds(models.Model):
    user = models.ForeignKey(User)  
    device_id = models.CharField(max_length=15)
    def __str__(self):
        return self.device_id

class UserProfile(models.Model):
    user = models.OneToOneField(User)
    referral_code = models.CharField(max_length=8, unique=True)
    def __str__(self):
        return str(self.mal_rank)

而且,我在api.py中有以下代码:

class CreateUserProfileResource(ModelResource):
    user = fields.ForeignKey('users.api.CreateUserResource', 'user', full=True)
    class Meta:
        list_allowed_methods = ['get','post']
        always_return_data = True
        authorization = Authorization()
        authentication = Authentication()
        validation = CreateUserProfileValidation()
        resource_name = 'auth'
        queryset = UserProfile.objects.all()
    def hydrate(self, bundle):
        bundle.data["user"]['username'] = bundle.data.get('country_code') + bundle.data.get("user")['username']
        # Set a password automatically:
        raw_password = ''.join(random.choice(string.ascii_lowercase) for i in range(8))
        u = User(username='dummy')
        u.set_password(raw_password)
        bundle.data["user"]['password'] = u.password
        return bundle

class CreateUserResource(ModelResource):
    class Meta:
        authorization = Authorization()
        authentication = Authentication()
        always_return_data = True
        resource_name = 'user'
        queryset = User.objects.all()
        excludes = ['is_active', 'is_staff', 'is_superuser', 'date_joined', 'last_login']

当我向POST发送http://127.0.0.1:8000/api/v1/auth请求时(例如:

    curl -X POST -H "Content-Type: application/json" -d '{"user": {"email":"a@b.com","username":"abcdef"}, "referral_code":"abc123"}' http://127.0.0.1:8000/api/v1/auth

然后成功创建了UserUserProfile对象。但我还想在同一个端点创建一个UserDeviceIds对象。我尝试过组合不同的Tastypie Relationship Fields但我无法创建UserDeviceIds对象。有人可以用一些示例代码详细说明Tastypie关系字段,以便让我了解Django中关系的工作原理吗?

例如,我在CreateUserProfileResource中修改了api.py并添加了以下内容:

    deviceid = fields.ForeignKey('users.api.CreateUserDeviceIdsResource', 'deviceid', full=True)

以便CreateUserProfileResource现在看起来像:

class CreateUserProfileResource(ModelResource):
    user = fields.ForeignKey('users.api.CreateUserResource', 'user', full=True)
    deviceid = fields.ForeignKey('users.api.CreateUserDeviceIdsResource', 'deviceid', full=True)
    class Meta:
        list_allowed_methods = ['get','post']
        ...
        ...

并为UserDeviceIds模型添加了新资源:

class CreateUserDeviceIdsResource(ModelResource):
    class Meta:
        authorization = Authorization()
        authentication = Authentication()
        always_return_data = True
        resource_name = 'deviceid'
        queryset = UserDeviceIds.objects.all()

当我尝试发送POST请求时:

    curl -X POST -H "Content-Type: application/json" -d '{"user": {"email":"a@b.com","username":"abcdef"}, "referral_code":"abc123", "deviceid": {"device_id": "abc"}}' http://127.0.0.1:8000/api/v1/auth

我得到以下错误追溯:

    {"error_message": "null value in column \"user_id\" violates not-null constraint
    DETAIL:  Failing row contains (2, abc, def, ghi, null).
    ", "traceback": "Traceback (most recent call last):

      File \"/vagrant/venv/lib/python3.4/site-packages/django/db/backends/utils.py\", line 65, in execute
        return self.cursor.execute(sql, params)

    psycopg2.IntegrityError: null value in column \"user_id\" violates not-null constraint
    DETAIL:  Failing row contains (2, abc, def, ghi, null).

    The above exception was the direct cause of the following exception:

    Traceback (most recent call last):
      File \"/vagrant/venv/lib/python3.4/site-packages/tastypie/resources.py\", line 201, in wrapper
        response = callback(request, *args, **kwargs)
      File \"/vagrant/venv/lib/python3.4/site-packages/tastypie/resources.py\", line 432, in dispatch_list
        return self.dispatch('list', request, **kwargs)
      File \"/vagrant/venv/lib/python3.4/site-packages/tastypie/resources.py\", line 464, in dispatch
        response = method(request, **kwargs)
      File \"/vagrant/venv/lib/python3.4/site-packages/tastypie/resources.py\", line 1340, in post_list
        updated_bundle = self.obj_create(bundle, **self.remove_api_resource_names(kwargs))
      File \"/vagrant/venv/lib/python3.4/site-packages/tastypie/resources.py\", line 2104, in obj_create
        return self.save(bundle)
      File \"/vagrant/venv/lib/python3.4/site-packages/tastypie/resources.py\", line 2247, in save
        self.save_related(bundle)
      File \"/vagrant/venv/lib/python3.4/site-packages/tastypie/resources.py\", line 2318, in save_related
        related_resource.save(related_bundle)
      File \"/vagrant/venv/lib/python3.4/site-packages/tastypie/resources.py\", line 2250, in save
        bundle.obj.save()
      File \"/vagrant/venv/lib/python3.4/site-packages/django/db/models/base.py\", line 589, in save
        force_update=force_update, update_fields=update_fields)
      File \"/vagrant/venv/lib/python3.4/site-packages/django/db/models/base.py\", line 617, in save_base
        updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)
      File \"/vagrant/venv/lib/python3.4/site-packages/django/db/models/base.py\", line 698, in _save_table
        result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)
      File \"/vagrant/venv/lib/python3.4/site-packages/django/db/models/base.py\", line 731, in _do_insert
        using=using, raw=raw)
      File \"/vagrant/venv/lib/python3.4/site-packages/django/db/models/manager.py\", line 92, in manager_method
        return getattr(self.get_queryset(), name)(*args, **kwargs)
      File \"/vagrant/venv/lib/python3.4/site-packages/django/db/models/query.py\", line 921, in _insert
        return query.get_compiler(using=using).execute_sql(return_id)
      File \"/vagrant/venv/lib/python3.4/site-packages/django/db/models/sql/compiler.py\", line 920, in execute_sql
        cursor.execute(sql, params)
      File \"/vagrant/venv/lib/python3.4/site-packages/django/db/backends/utils.py\", line 81, in execute
        return super(CursorDebugWrapper, self).execute(sql, params)
      File \"/vagrant/venv/lib/python3.4/site-packages/django/db/backends/utils.py\", line 65, in execute
        return self.cursor.execute(sql, params)
      File \"/vagrant/venv/lib/python3.4/site-packages/django/db/utils.py\", line 94, in __exit__
        six.reraise(dj_exc_type, dj_exc_value, traceback)
      File \"/vagrant/venv/lib/python3.4/site-packages/django/utils/six.py\", line 658, in reraise
        raise value.with_traceback(tb)
      File \"/vagrant/venv/lib/python3.4/site-packages/django/db/backends/utils.py\", line 65, in execute
        return self.cursor.execute(sql, params)
    django.db.utils.IntegrityError: null value in column \"user_id\" violates not-null constraint
    DETAIL:  Failing row contains (2, abc, def, ghi, null).
    "}

我理解这意味着当它试图保存UserDeviceIds对象时,它无法在bundle中找到数据以放入ForeignKey(User)模型的UserDeviceIds字段。

有人可以解释为使这段代码有用而需要做些什么吗?

谢谢!

1 个答案:

答案 0 :(得分:0)

你无法在当前的关系中实现它。模型资源是模型的真实反映。例如,您可以创建:

>>> UserProfile.objects.create(refferal_code='asdfasdf', user=User.objects.create_user(username='asdfasdf'))

或者这个:

>>> UserDeviceIds.objects.create(device_id='adfadf', user=User.objects.create_user(username='asdfasdfas'))

但不能同时在一条指令中。

REST API非常棒,因为它非常适合Django ORM自己的界面。

因此,您可以创建具有子项的对象,但不能同时创建其他父项。

您已将具有功能目的creation的资源命名为明确暗示某些内容不正确的资源。您的资源应仅分配给模型。

在我看来,REST API非常适合使用,因为它清晰,易于理解,易于管理,易于构建,易于测试。但Django并没有很好地报名。

如果您不得不打破一些RESTful规则,请不要尝试重新定义现有的完整功能资源,这会给您带来很多痛苦。而是为此目的很好地描述一些自定义的分离端点。

我建议创建自定义视图,例如端点处理json / xml,无论您支持注册。如果您将来找到更好的解决方案,您可以轻松更换它。我建议这样做是因为:1。在一次通话中创建这3个模型至关重要,如果互联网起飞,它将打破模型。 2.会更快。

我怀疑存在解决问题的现成应用程序,因为每个网站的唱歌总是有点独特。

import json

from django.contrib.auth.models import User
from django.http import (HttpResponse, HttpResponseBadRequest,
                         HttpResponseNotAllowed)
from django.views.decorators.csrf import csrf_exempt


@csrf_exempt
def signup(request):
    """
    Description:
    POST faldksfalskdfjalksdf

    Headers:
    sdfasdf

    Attributes:
    sadljfkasdlfksad

    Responses:
    asdf
    """
    if request.method != 'POST':
        return HttpResponseNotAllowed(permitted_methods=('POST',))
    try:
        data = json.loads(request.body)
    except (AttributeError, ValueError):
        return HttpResponseBadRequest(
            json.dumps({'error': 'JSON format invalid',
                        'success': False}),
            content_type='application/json')


    if data.get('username', None) and data.get('device_id', None) and data.get('country', None):
        # add more validation.
        user = User.objects.create_user(username=data['username'])
        UserProfile.objects.create(refferal_code='asdfasdf', user=user)
        UserDeviceIds.objects.create(device_id='adfadf', user=user)
        return HttpResponse(json.dumps({'success': True}),
                            content_type='application/json')

    else:
        return HttpResponseBadRequest(
            json.dumps({'error': 'username, device_id or country attributes missing.',
                        'success': False}),
            content_type='application/json')