While trying to post mptt model via django rest framework getting ValueError: Cannot use None as a query value
While trying to post mptt model via django rest framework getting ValueError: Cannot use None as a query value
这是我尝试为其编写测试的视图:
class RestaurantsTreeView(generics.ListCreateAPIView):
serializer_class = RestarauntsTreeSerializer
def get_serializer_class(self):
from rest_framework import serializers
if self.request.method == 'GET':
return RestarauntsTreeSerializer
parent_choices = self.request.user.restaurants_set.filter(status=Restaurants.VISIBLE)
class NestedRestaurantDetailSerializer(serializers.ModelSerializer):
parent_id = serializers.RelatedField(queryset=parent_choices, allow_null=True, required=False)
class Meta:
model = Restaurants
fields = ("id", "name", "parent_id")
return NestedRestaurantDetailSerializer
这是我试图通过 POST 请求创建的模型:
from mptt.models import MPTTModel, TreeForeignKey
class Restaurants(MPTTModel):
name = models.CharField(max_length=255, blank=True, null=True)
parent = TreeForeignKey('self', null=True, blank=True, related_name='children', db_index=True)
class MPTTMeta:
order_insertion_by = ['name']
def __str__(self):
return self.name
最后是我的测试:
class CreateRestaurantTestCase(TestCase):
def setUp(self):
self.user = UserFactory.create()
self.user.user_permissions.add(Permission.objects.get(codename='add_restaurants'))
self.client = APIClient()
self.client.force_authenticate(user=self.user)
def test_authorization_required(self):
response = self.client.post(reverse('api_v1:restaurants_list'), data={
"parent_id": None,
"name": "fake restaurant",
})
self.assertEqual(response.status_code, 401)
那个returns那个错误:
Error
Traceback (most recent call last):
File "project/cafe/tests/api/test_restaurants.py", line 26, in test_required_fields
response = self.client.post(reverse('api_v1:restaurants_list'), data={})
File "env/lib/python3.5/site-packages/rest_framework/test.py", line 299, in post
path, data=data, format=format, content_type=content_type, **extra)
File "env/lib/python3.5/site-packages/rest_framework/test.py", line 212, in post
return self.generic('POST', path, data, content_type, **extra)
File "env/lib/python3.5/site-packages/rest_framework/test.py", line 237, in generic
method, path, data, content_type, secure, **extra)
File "env/lib/python3.5/site-packages/django/test/client.py", line 416, in generic
return self.request(**r)
File "env/lib/python3.5/site-packages/rest_framework/test.py", line 288, in request
return super(APIClient, self).request(**kwargs)
File "env/lib/python3.5/site-packages/rest_framework/test.py", line 240, in request
request = super(APIRequestFactory, self).request(**kwargs)
File "env/lib/python3.5/site-packages/django/test/client.py", line 501, in request
six.reraise(*exc_info)
File "env/lib/python3.5/site-packages/django/utils/six.py", line 686, in reraise
raise value
File "env/lib/python3.5/site-packages/django/core/handlers/exception.py", line 41, in inner
response = get_response(request)
File "env/lib/python3.5/site-packages/django/core/handlers/base.py", line 187, in _get_response
response = self.process_exception_by_middleware(e, request)
File "env/lib/python3.5/site-packages/django/core/handlers/base.py", line 185, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "env/lib/python3.5/site-packages/django/views/decorators/csrf.py", line 58, in wrapped_view
return view_func(*args, **kwargs)
File "env/lib/python3.5/site-packages/django/views/generic/base.py", line 68, in view
return self.dispatch(request, *args, **kwargs)
File "env/lib/python3.5/site-packages/rest_framework/views.py", line 489, in dispatch
response = self.handle_exception(exc)
File "env/lib/python3.5/site-packages/rest_framework/views.py", line 449, in handle_exception
self.raise_uncaught_exception(exc)
File "env/lib/python3.5/site-packages/rest_framework/views.py", line 486, in dispatch
response = handler(request, *args, **kwargs)
File "env/lib/python3.5/site-packages/rest_framework/generics.py", line 244, in post
return self.create(request, *args, **kwargs)
File "env/lib/python3.5/site-packages/rest_framework/mixins.py", line 21, in create
self.perform_create(serializer)
File "env/lib/python3.5/site-packages/rest_framework/mixins.py", line 26, in perform_create
serializer.save()
File "env/lib/python3.5/site-packages/rest_framework/serializers.py", line 214, in save
self.instance = self.create(validated_data)
File "env/lib/python3.5/site-packages/rest_framework/serializers.py", line 913, in create
instance = ModelClass.objects.create(**validated_data)
File "env/lib/python3.5/site-packages/django/db/models/manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "env/lib/python3.5/site-packages/django/db/models/query.py", line 394, in create
obj.save(force_insert=True, using=self.db)
File "env/lib/python3.5/site-packages/mptt/models.py", line 977, in save
right_sibling = opts.get_ordered_insertion_target(self, parent)
File "env/lib/python3.5/site-packages/mptt/models.py", line 216, in get_ordered_insertion_target
queryset = node.__class__._tree_manager.db_manager(node._state.db).filter(filters).order_by(*order_by)
File "env/lib/python3.5/site-packages/django/db/models/manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "env/lib/python3.5/site-packages/django/db/models/query.py", line 784, in filter
return self._filter_or_exclude(False, *args, **kwargs)
File "env/lib/python3.5/site-packages/django/db/models/query.py", line 802, in _filter_or_exclude
clone.query.add_q(Q(*args, **kwargs))
File "env/lib/python3.5/site-packages/django/db/models/sql/query.py", line 1250, in add_q
clause, _ = self._add_q(q_object, self.used_aliases)
File "env/lib/python3.5/site-packages/django/db/models/sql/query.py", line 1270, in _add_q
current_negated, allow_joins, split_subq)
File "env/lib/python3.5/site-packages/django/db/models/sql/query.py", line 1276, in _add_q
allow_joins=allow_joins, split_subq=split_subq,
File "env/lib/python3.5/site-packages/django/db/models/sql/query.py", line 1160, in build_filter
value, lookups, used_joins = self.prepare_lookup_value(value, lookups, can_reuse, allow_joins)
File "env/lib/python3.5/site-packages/django/db/models/sql/query.py", line 989, in prepare_lookup_value
raise ValueError("Cannot use None as a query value")
ValueError: Cannot use None as a query value
Destroying test database for alias 'default'...
Process finished with exit code 1
我希望能够创建父字段为空的餐厅。它可以是 post 没有父字段或有空的父字段。谢谢
django 1.11、drf 3.7、mptt 0.8.7
首先,您将序列化器定义放在视图中这一事实使您的代码不太可读...您可以修改 parent_id
字段的查询集,同时覆盖 get_serializer
方法你的看法。
# serializers.py
from rest_framework import serializers
class NestedRestaurantDetailSerializer(serializers.ModelSerializer):
# We don't care about filtering the queryset here, we override it in the view
parent_id = serializers.RelatedField(queryset=Restaurant.objects.all(), allow_null=True, required=False)
class Meta:
model = Restaurants
fields = ("id", "name", "parent_id")
# views.py
from .serializers import (
RestaurantsTreeSerializer,
NestedRestaurantDetailSerializer
)
class RestaurantsTreeView(generics.ListCreateAPIView):
def get_serializer(self, *args, **kwargs):
if self.request.method == 'GET':
kwargs['context'] = self.get_serializer_context()
return RestaurantsTreeSerializer(*args, **kwargs)
else:
kwargs['context'] = self.get_serializer_context()
nested_serializer = NestedRestaurantDetailSerializer(*args, **kwargs)
# Here we modify the queryset of the `parent_id` field
nested_serializer.fields['parent_id'].queryset = self.request.user.restaurants_set.filter(status=Restaurants.VISIBLE)
return nested_serializer
其次,关于您的异常,在documentation of django-mptt
中,您可以阅读以下内容:
order_insertion_by
A list of field names which should define ordering when new tree nodes are being inserted or existing nodes are being reparented, with the most significant ordering field name first. Defaults to [].
It is assumed that any field identified as defining ordering will never be NULL in the database.
上面是重要的一行。在您的模型定义中,您有:
name = models.CharField(max_length=255, blank=True, null=True)
您将可为空的字段定义为 order_insertion_by
属性的参数。你最后的例外是 Cannot use None as a query value
的事实让我认为它是有联系的...
未显示异常起源处的测试方法代码,但我假设您使用 test_required_fields
之类的名称尝试向您的 enpoint 发送一个空的 name
参数。因此 Cannot use None as a query value
。由于您在模型中将 name
字段定义为 nullable
,因此您的序列化程序也将其标记为 nullable
。
这是我尝试为其编写测试的视图:
class RestaurantsTreeView(generics.ListCreateAPIView):
serializer_class = RestarauntsTreeSerializer
def get_serializer_class(self):
from rest_framework import serializers
if self.request.method == 'GET':
return RestarauntsTreeSerializer
parent_choices = self.request.user.restaurants_set.filter(status=Restaurants.VISIBLE)
class NestedRestaurantDetailSerializer(serializers.ModelSerializer):
parent_id = serializers.RelatedField(queryset=parent_choices, allow_null=True, required=False)
class Meta:
model = Restaurants
fields = ("id", "name", "parent_id")
return NestedRestaurantDetailSerializer
这是我试图通过 POST 请求创建的模型:
from mptt.models import MPTTModel, TreeForeignKey
class Restaurants(MPTTModel):
name = models.CharField(max_length=255, blank=True, null=True)
parent = TreeForeignKey('self', null=True, blank=True, related_name='children', db_index=True)
class MPTTMeta:
order_insertion_by = ['name']
def __str__(self):
return self.name
最后是我的测试:
class CreateRestaurantTestCase(TestCase):
def setUp(self):
self.user = UserFactory.create()
self.user.user_permissions.add(Permission.objects.get(codename='add_restaurants'))
self.client = APIClient()
self.client.force_authenticate(user=self.user)
def test_authorization_required(self):
response = self.client.post(reverse('api_v1:restaurants_list'), data={
"parent_id": None,
"name": "fake restaurant",
})
self.assertEqual(response.status_code, 401)
那个returns那个错误:
Error
Traceback (most recent call last):
File "project/cafe/tests/api/test_restaurants.py", line 26, in test_required_fields
response = self.client.post(reverse('api_v1:restaurants_list'), data={})
File "env/lib/python3.5/site-packages/rest_framework/test.py", line 299, in post
path, data=data, format=format, content_type=content_type, **extra)
File "env/lib/python3.5/site-packages/rest_framework/test.py", line 212, in post
return self.generic('POST', path, data, content_type, **extra)
File "env/lib/python3.5/site-packages/rest_framework/test.py", line 237, in generic
method, path, data, content_type, secure, **extra)
File "env/lib/python3.5/site-packages/django/test/client.py", line 416, in generic
return self.request(**r)
File "env/lib/python3.5/site-packages/rest_framework/test.py", line 288, in request
return super(APIClient, self).request(**kwargs)
File "env/lib/python3.5/site-packages/rest_framework/test.py", line 240, in request
request = super(APIRequestFactory, self).request(**kwargs)
File "env/lib/python3.5/site-packages/django/test/client.py", line 501, in request
six.reraise(*exc_info)
File "env/lib/python3.5/site-packages/django/utils/six.py", line 686, in reraise
raise value
File "env/lib/python3.5/site-packages/django/core/handlers/exception.py", line 41, in inner
response = get_response(request)
File "env/lib/python3.5/site-packages/django/core/handlers/base.py", line 187, in _get_response
response = self.process_exception_by_middleware(e, request)
File "env/lib/python3.5/site-packages/django/core/handlers/base.py", line 185, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "env/lib/python3.5/site-packages/django/views/decorators/csrf.py", line 58, in wrapped_view
return view_func(*args, **kwargs)
File "env/lib/python3.5/site-packages/django/views/generic/base.py", line 68, in view
return self.dispatch(request, *args, **kwargs)
File "env/lib/python3.5/site-packages/rest_framework/views.py", line 489, in dispatch
response = self.handle_exception(exc)
File "env/lib/python3.5/site-packages/rest_framework/views.py", line 449, in handle_exception
self.raise_uncaught_exception(exc)
File "env/lib/python3.5/site-packages/rest_framework/views.py", line 486, in dispatch
response = handler(request, *args, **kwargs)
File "env/lib/python3.5/site-packages/rest_framework/generics.py", line 244, in post
return self.create(request, *args, **kwargs)
File "env/lib/python3.5/site-packages/rest_framework/mixins.py", line 21, in create
self.perform_create(serializer)
File "env/lib/python3.5/site-packages/rest_framework/mixins.py", line 26, in perform_create
serializer.save()
File "env/lib/python3.5/site-packages/rest_framework/serializers.py", line 214, in save
self.instance = self.create(validated_data)
File "env/lib/python3.5/site-packages/rest_framework/serializers.py", line 913, in create
instance = ModelClass.objects.create(**validated_data)
File "env/lib/python3.5/site-packages/django/db/models/manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "env/lib/python3.5/site-packages/django/db/models/query.py", line 394, in create
obj.save(force_insert=True, using=self.db)
File "env/lib/python3.5/site-packages/mptt/models.py", line 977, in save
right_sibling = opts.get_ordered_insertion_target(self, parent)
File "env/lib/python3.5/site-packages/mptt/models.py", line 216, in get_ordered_insertion_target
queryset = node.__class__._tree_manager.db_manager(node._state.db).filter(filters).order_by(*order_by)
File "env/lib/python3.5/site-packages/django/db/models/manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "env/lib/python3.5/site-packages/django/db/models/query.py", line 784, in filter
return self._filter_or_exclude(False, *args, **kwargs)
File "env/lib/python3.5/site-packages/django/db/models/query.py", line 802, in _filter_or_exclude
clone.query.add_q(Q(*args, **kwargs))
File "env/lib/python3.5/site-packages/django/db/models/sql/query.py", line 1250, in add_q
clause, _ = self._add_q(q_object, self.used_aliases)
File "env/lib/python3.5/site-packages/django/db/models/sql/query.py", line 1270, in _add_q
current_negated, allow_joins, split_subq)
File "env/lib/python3.5/site-packages/django/db/models/sql/query.py", line 1276, in _add_q
allow_joins=allow_joins, split_subq=split_subq,
File "env/lib/python3.5/site-packages/django/db/models/sql/query.py", line 1160, in build_filter
value, lookups, used_joins = self.prepare_lookup_value(value, lookups, can_reuse, allow_joins)
File "env/lib/python3.5/site-packages/django/db/models/sql/query.py", line 989, in prepare_lookup_value
raise ValueError("Cannot use None as a query value")
ValueError: Cannot use None as a query value
Destroying test database for alias 'default'...
Process finished with exit code 1
我希望能够创建父字段为空的餐厅。它可以是 post 没有父字段或有空的父字段。谢谢 django 1.11、drf 3.7、mptt 0.8.7
首先,您将序列化器定义放在视图中这一事实使您的代码不太可读...您可以修改 parent_id
字段的查询集,同时覆盖 get_serializer
方法你的看法。
# serializers.py
from rest_framework import serializers
class NestedRestaurantDetailSerializer(serializers.ModelSerializer):
# We don't care about filtering the queryset here, we override it in the view
parent_id = serializers.RelatedField(queryset=Restaurant.objects.all(), allow_null=True, required=False)
class Meta:
model = Restaurants
fields = ("id", "name", "parent_id")
# views.py
from .serializers import (
RestaurantsTreeSerializer,
NestedRestaurantDetailSerializer
)
class RestaurantsTreeView(generics.ListCreateAPIView):
def get_serializer(self, *args, **kwargs):
if self.request.method == 'GET':
kwargs['context'] = self.get_serializer_context()
return RestaurantsTreeSerializer(*args, **kwargs)
else:
kwargs['context'] = self.get_serializer_context()
nested_serializer = NestedRestaurantDetailSerializer(*args, **kwargs)
# Here we modify the queryset of the `parent_id` field
nested_serializer.fields['parent_id'].queryset = self.request.user.restaurants_set.filter(status=Restaurants.VISIBLE)
return nested_serializer
其次,关于您的异常,在documentation of django-mptt
中,您可以阅读以下内容:
order_insertion_by
A list of field names which should define ordering when new tree nodes are being inserted or existing nodes are being reparented, with the most significant ordering field name first. Defaults to [].
It is assumed that any field identified as defining ordering will never be NULL in the database.
上面是重要的一行。在您的模型定义中,您有:
name = models.CharField(max_length=255, blank=True, null=True)
您将可为空的字段定义为 order_insertion_by
属性的参数。你最后的例外是 Cannot use None as a query value
的事实让我认为它是有联系的...
未显示异常起源处的测试方法代码,但我假设您使用 test_required_fields
之类的名称尝试向您的 enpoint 发送一个空的 name
参数。因此 Cannot use None as a query value
。由于您在模型中将 name
字段定义为 nullable
,因此您的序列化程序也将其标记为 nullable
。