我正在学习使用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
然后成功创建了User
和UserProfile
对象。但我还想在同一个端点创建一个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
字段。
有人可以解释为使这段代码有用而需要做些什么吗?
谢谢!
答案 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')