标签归档:django-rest-framework

在django-rest-framework中禁用ViewSet中的方法

问题:在django-rest-framework中禁用ViewSet中的方法

ViewSets 具有自动列出,检索,创建,更新,删除,…的方法

我想禁用其中一些,我想出的解决方案可能不是一个好方法,因为OPTIONS仍然指出了允许的范围。

关于如何正确执行此操作的任何想法吗?

class SampleViewSet(viewsets.ModelViewSet):
    queryset = api_models.Sample.objects.all()
    serializer_class = api_serializers.SampleSerializer

    def list(self, request):
        return Response(status=status.HTTP_405_METHOD_NOT_ALLOWED)
    def create(self, request):
        return Response(status=status.HTTP_405_METHOD_NOT_ALLOWED)

ViewSets have automatic methods to list, retrieve, create, update, delete, …

I would like to disable some of those, and the solution I came up with is probably not a good one, since OPTIONS still states those as allowed.

Any idea on how to do this the right way?

class SampleViewSet(viewsets.ModelViewSet):
    queryset = api_models.Sample.objects.all()
    serializer_class = api_serializers.SampleSerializer

    def list(self, request):
        return Response(status=status.HTTP_405_METHOD_NOT_ALLOWED)
    def create(self, request):
        return Response(status=status.HTTP_405_METHOD_NOT_ALLOWED)

回答 0

的定义ModelViewSet是:

class ModelViewSet(mixins.CreateModelMixin, 
                   mixins.RetrieveModelMixin, 
                   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
                   mixins.ListModelMixin,
                   GenericViewSet)

因此,除了扩展之外ModelViewSet,为什么不随便使用您需要的东西呢?因此,例如:

from rest_framework import viewsets, mixins

class SampleViewSet(mixins.RetrieveModelMixin,
                    mixins.UpdateModelMixin,
                    mixins.DestroyModelMixin,
                    viewsets.GenericViewSet):
    ...

使用这种方法,路由器应该只为所包含的方法生成路由。

参考

模型视图集

The definition of ModelViewSet is:

class ModelViewSet(mixins.CreateModelMixin, 
                   mixins.RetrieveModelMixin, 
                   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
                   mixins.ListModelMixin,
                   GenericViewSet)

So rather than extending ModelViewSet, why not just use whatever you need? So for example:

from rest_framework import viewsets, mixins

class SampleViewSet(mixins.RetrieveModelMixin,
                    mixins.UpdateModelMixin,
                    mixins.DestroyModelMixin,
                    viewsets.GenericViewSet):
    ...

With this approach, the router should only generate routes for the included methods.

Reference:

ModelViewSet


回答 1

您可以继续使用viewsets.ModelViewSethttp_method_names在ViewSet上进行定义。

class SampleViewSet(viewsets.ModelViewSet):
    queryset = api_models.Sample.objects.all()
    serializer_class = api_serializers.SampleSerializer
    http_method_names = ['get', 'post', 'head']

一旦你加入http_method_names,你将无法做到putpatch了。

如果您想要put但不想要patch,您可以保留http_method_names = ['get', 'post', 'head', 'put']

在内部,DRF视图从Django CBV扩展。Django CBV具有一个名为http_method_names的属性。因此,您也可以在DRF视图中使用http_method_names。

[Shameless Plug]:如果此答案有用,您将喜欢我在DRF上的系列文章,网址https://www.agiliq.com/blog/2019/04/drf-polls/

You could keep using viewsets.ModelViewSet and define http_method_names on your ViewSet.

Example

class SampleViewSet(viewsets.ModelViewSet):
    queryset = api_models.Sample.objects.all()
    serializer_class = api_serializers.SampleSerializer
    http_method_names = ['get', 'post', 'head']

Once you add http_method_names, you will not be able to do put and patch anymore.

If you want put but don’t want patch, you can keep http_method_names = ['get', 'post', 'head', 'put']

Internally, DRF Views extend from Django CBV. Django CBV has an attribute called http_method_names. So you can use http_method_names with DRF views too.

[Shameless Plug]: If this answer was helpful, you will like my series of posts on DRF at https://www.agiliq.com/blog/2019/04/drf-polls/.


回答 2

尽管这篇文章已经有一段时间了,但我突然发现实际上它们是禁用这些功能的一种方法,您可以直接在views.py中对其进行编辑。

资料来源:https : //www.django-rest-framework.org/api-guide/viewsets/#viewset-actions

from rest_framework import viewsets, status
from rest_framework.response import Response

class NameWhateverYouWantViewSet(viewsets.ModelViewSet):

    def create(self, request):
        response = {'message': 'Create function is not offered in this path.'}
        return Response(response, status=status.HTTP_403_FORBIDDEN)

    def update(self, request, pk=None):
        response = {'message': 'Update function is not offered in this path.'}
        return Response(response, status=status.HTTP_403_FORBIDDEN)

    def partial_update(self, request, pk=None):
        response = {'message': 'Update function is not offered in this path.'}
        return Response(response, status=status.HTTP_403_FORBIDDEN)

    def destroy(self, request, pk=None):
        response = {'message': 'Delete function is not offered in this path.'}
        return Response(response, status=status.HTTP_403_FORBIDDEN)

Although it’s been a while for this post, I suddenly found out that actually there is a way to disable those functions, you can edit it in the views.py directly.

Source: https://www.django-rest-framework.org/api-guide/viewsets/#viewset-actions

from rest_framework import viewsets, status
from rest_framework.response import Response

class NameThisClassWhateverYouWantViewSet(viewsets.ModelViewSet):

    def create(self, request):
        response = {'message': 'Create function is not offered in this path.'}
        return Response(response, status=status.HTTP_403_FORBIDDEN)

    def update(self, request, pk=None):
        response = {'message': 'Update function is not offered in this path.'}
        return Response(response, status=status.HTTP_403_FORBIDDEN)

    def partial_update(self, request, pk=None):
        response = {'message': 'Update function is not offered in this path.'}
        return Response(response, status=status.HTTP_403_FORBIDDEN)

    def destroy(self, request, pk=None):
        response = {'message': 'Delete function is not offered in this path.'}
        return Response(response, status=status.HTTP_403_FORBIDDEN)

回答 3

如果尝试从DRF视图集中禁用PUT方法,则可以创建一个自定义路由器:

from rest_framework.routers import DefaultRouter

class NoPutRouter(DefaultRouter):
    """
    Router class that disables the PUT method.
    """
    def get_method_map(self, viewset, method_map):

        bound_methods = super().get_method_map(viewset, method_map)

        if 'put' in bound_methods.keys():
            del bound_methods['put']

        return bound_methods

通过在路由器上禁用该方法,您的api模式文档将是正确的。

If you are trying to disable the PUT method from a DRF viewset, you can create a custom router:

from rest_framework.routers import DefaultRouter

class NoPutRouter(DefaultRouter):
    """
    Router class that disables the PUT method.
    """
    def get_method_map(self, viewset, method_map):

        bound_methods = super().get_method_map(viewset, method_map)

        if 'put' in bound_methods.keys():
            del bound_methods['put']

        return bound_methods

By disabling the method at the router, your api schema documentation will be correct.


回答 4

如何在DRF中为ViewSet禁用“删除”方法

class YourViewSet(viewsets.ModelViewSet):
    def _allowed_methods(self):
        return [m for m in super(YourViewSet, self)._allowed_methods() if m not in ['DELETE']]

PS这比显式指定所有必需的方法更可靠,因此很少有机会忘记一些重要的方法OPTIONS,HEAD等

默认情况下,DPS的PPS具有 http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']

How to disable “DELETE” method for ViewSet in DRF

class YourViewSet(viewsets.ModelViewSet):
    def _allowed_methods(self):
        return [m for m in super(YourViewSet, self)._allowed_methods() if m not in ['DELETE']]

P.S. This is more reliable than explicitly specifying all the necessary methods, so there is less chance of forgetting some of important methods OPTIONS, HEAD, etc

P.P.S. by default DRF has http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']


回答 5

在Django Rest Framework 3.xx中,您可以ModelViewSet通过将字典传递给as_view方法来简单地启用要启用的每个方法。在此字典中,键必须包含请求类型(GET,POST,DELETE等),并且值必须包含相应的方法名称(列表,检索,更新等)。例如,假设您要Sample创建或读取模型,但不希望对其进行修改。因此,这意味着你想listretrievecreate方法,是使(和你希望别人被禁用。)

您需要做的就是添加如下路径urlpatterns

path('sample/', SampleViewSet.as_view({
    'get': 'list',
    'post': 'create'
})),
path('sample/<pk>/', SampleViewSet.as_view({  # for get sample by id.
    'get': 'retrieve'
}))

如您所见,上述路由设置中没有no deleteputrequest,因此例如,如果您将put请求发送到url,它将以405响应您Method Not Allowed

{
    "detail": "Method \"PUT\" not allowed."
}

In Django Rest Framework 3.x.x you can simply enable every each method you want to be enabled for ModelViewSet, by passing a dictionary to as_view method. In this dictionary, the key must contain request type (GET, POST, DELETE, etc) and the value must contain corresponding method name (list, retrieve, update, etc). For example let say you want Sample model to be created or read but you don’t want it to be modified. So it means you want list, retrieve and create method to be enable (and you want others to be disabled.)

All you need to do is to add paths to urlpatterns like these:

path('sample/', SampleViewSet.as_view({
    'get': 'list',
    'post': 'create'
})),
path('sample/<pk>/', SampleViewSet.as_view({  # for get sample by id.
    'get': 'retrieve'
}))

As you can see there’s no delete and put request in above routing settings, so for example if you send a put request to the url, it response you with 405 Method Not Allowed:

{
    "detail": "Method \"PUT\" not allowed."
}

回答 6

如果您打算禁用放置/发布/销毁方法,则可以使用

viewsets.ReadOnlyModelViewSet https://www.django-rest-framework.org/tutorial/6-viewsets-and-routers/#refactoring-to-use-viewsets

If you are planning to disable put/post/destroy methods, you can use

viewsets.ReadOnlyModelViewSet https://www.django-rest-framework.org/tutorial/6-viewsets-and-routers/#refactoring-to-use-viewsets


TemplateDoesNotExist-Django错误

问题:TemplateDoesNotExist-Django错误

我正在使用Django Rest Framework。而且我不断出错

Exception Type: TemplateDoesNotExist
Exception Value: rest_framework/api.html

我不知道我怎么了。这是我第一次尝试使用REST Framework。这是代码。

views.py

import socket, json
from modules.data.models import *
from modules.utils import *
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from modules.actions.serializers import ActionSerializer


@api_view(['POST'])
@check_field_exists_wrapper("installation")
def api_actions(request, format = None):

    action_type = request.POST['action_type']
    if action_type == "Shutdown" : 
        send_message = '1'
        print "Shutting Down the system..."
    elif action_type == "Enable" : 
        send_message = '1'
        print "Enabling the system..."
    elif action_type == "Disable" : 
        send_message = '1'
        print "Disabling the system..."
    elif action_type == "Restart" : 
        send_message = '1'
        print "Restarting the system..."

    if action_type in ["Shutdown", "Enable", "Disable"] : PORT = 6000
    else : PORT = 6100

    controllers_list = Controller.objects.filter(installation_id = kwargs['installation_id'])

    for controller_obj in controllers_list:
        ip = controller_obj.ip
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.connect((ip, PORT))
            s.send(send_message)
            s.close()
        except Exception as e:
            print("Exception when sending " + action_type +" command: "+str(e))

    return Response(status = status.HTTP_200_OK)

models.py

class Controller(models.Model):
    id = models.IntegerField(primary_key = True)
    name = models.CharField(max_length = 255, unique = True)
    ip = models.CharField(max_length = 255, unique = True)
    installation_id = models.ForeignKey('Installation')

serializers.py

从django.forms中导入从rest_framework中导入小部件从modules.data.models中导入序列化器*

class ActionSerializer(serializers.ModelSerializer):
    class Meta:
        model = Controller
        fields = ('id', 'name', 'ip', 'installation_id')

urls.py

from django.conf.urls import patterns, url
from rest_framework.urlpatterns import format_suffix_patterns

urlpatterns = patterns('modules.actions.views',
    url(r'^$','api_actions',name='api_actions'),
)

I’m using Django Rest Framework. and I keep getting an error

Exception Type: TemplateDoesNotExist
Exception Value: rest_framework/api.html

I dont know how I’m going wrong. This is the first time I’m trying out hands on REST Framework. This is code.

views.py

import socket, json
from modules.data.models import *
from modules.utils import *
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from modules.actions.serializers import ActionSerializer


@api_view(['POST'])
@check_field_exists_wrapper("installation")
def api_actions(request, format = None):

    action_type = request.POST['action_type']
    if action_type == "Shutdown" : 
        send_message = '1'
        print "Shutting Down the system..."
    elif action_type == "Enable" : 
        send_message = '1'
        print "Enabling the system..."
    elif action_type == "Disable" : 
        send_message = '1'
        print "Disabling the system..."
    elif action_type == "Restart" : 
        send_message = '1'
        print "Restarting the system..."

    if action_type in ["Shutdown", "Enable", "Disable"] : PORT = 6000
    else : PORT = 6100

    controllers_list = Controller.objects.filter(installation_id = kwargs['installation_id'])

    for controller_obj in controllers_list:
        ip = controller_obj.ip
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.connect((ip, PORT))
            s.send(send_message)
            s.close()
        except Exception as e:
            print("Exception when sending " + action_type +" command: "+str(e))

    return Response(status = status.HTTP_200_OK)

models.py

class Controller(models.Model):
    id = models.IntegerField(primary_key = True)
    name = models.CharField(max_length = 255, unique = True)
    ip = models.CharField(max_length = 255, unique = True)
    installation_id = models.ForeignKey('Installation')

serializers.py

from django.forms import widgets from rest_framework import serializers from modules.data.models import *

class ActionSerializer(serializers.ModelSerializer):
    class Meta:
        model = Controller
        fields = ('id', 'name', 'ip', 'installation_id')

urls.py

from django.conf.urls import patterns, url
from rest_framework.urlpatterns import format_suffix_patterns

urlpatterns = patterns('modules.actions.views',
    url(r'^$','api_actions',name='api_actions'),
)

回答 0

确保您已rest_framework在中列出settings.py INSTALLED_APPS

Make sure you have rest_framework listed in your settings.py INSTALLED_APPS.


回答 1

对我来说,rest_framework/api.html由于安装损坏或其他未知原因,文件系统上实际上缺少该文件系统。重新安装djangorestframework解决了问题:

$ pip install --upgrade djangorestframework

For me, rest_framework/api.html was actually missing on the filesystem due to a corrupt installation or some other unknown reason. Reinstalling djangorestframework fixed the problem:

$ pip install --upgrade djangorestframework

回答 2

请注意,DRF尝试以请求的相同格式返回数据。在您的浏览器中,这很可能是HTML。要指定替代响应,请使用?format=参数。例如:?format=json

如其他受访者所述,TemplateDoesNotExist当您在浏览器中访问API端点并且未将rest_framework包含在已安装应用程序列表中时,通常会发生此错误。

如果您的应用程序列表中未包含DRF,但又不想使用HTML Admin DRF页面,请尝试使用其他格式来“避免”此错误消息。

来自此处的文档的更多信息:http : //www.django-rest-framework.org/topics/browsable-api/#formats

Please note that the DRF attempts to return data in the same format that was requested. From your browser, this is most likely HTML. To specify an alternative response, use the ?format= parameter. For example: ?format=json.

The TemplateDoesNotExist error occurs most commonly when you are visiting an API endpoint in your browser and you do not have the rest_framework included in your list of installed apps, as described by other respondents.

If you do not have DRF included in your list of apps, but don’t want to use the HTML Admin DRF page, try using an alternative format to ‘side-step’ this error message.

More info from the docs here: http://www.django-rest-framework.org/topics/browsable-api/#formats


回答 3

不是您的情况,而是为定制loaders了可能的原因Django。例如,如果您有设置(自以来Django 1.8):

TEMPLATES = [
{
    ...
    'OPTIONS': {
        'context_processors': [
            'django.template.context_processors.debug',
            'django.template.context_processors.request',
            'django.contrib.auth.context_processors.auth',
            'django.contrib.messages.context_processors.messages'
        ],
        'loaders': [
            'django.template.loaders.filesystem.Loader',
        ],
        ...
    }
}]

Django将不尝试一下使用模板的应用程序文件夹,因为你应该明确地添加django.template.loaders.app_directories.Loaderloaders了这一点。

请注意,默认情况下django.template.loaders.app_directories.Loader包含在中loaders

Not your case, but also possible reason is customized loaders for Django. For example, if you have in settings (since Django 1.8):

TEMPLATES = [
{
    ...
    'OPTIONS': {
        'context_processors': [
            'django.template.context_processors.debug',
            'django.template.context_processors.request',
            'django.contrib.auth.context_processors.auth',
            'django.contrib.messages.context_processors.messages'
        ],
        'loaders': [
            'django.template.loaders.filesystem.Loader',
        ],
        ...
    }
}]

Django will not try to look at applications folders with templates, because you should explicitly add django.template.loaders.app_directories.Loader into loaders for that.

Notice, that by default django.template.loaders.app_directories.Loader included into loaders.


回答 4

我遇到了同样的错误消息。就我而言,这是由于将后端设置为Jinja2。在我的设置文件中:

TEMPLATES = [
{
    'BACKEND': 'django.template.backends.jinja2.Jinja2',
...

将其更改回默认值可解决此问题:

TEMPLATES = [
{
    'BACKEND': 'django.template.backends.django.DjangoTemplates',
...

仍然不确定是否有办法将Jinja2后端与rest_framework一起使用。

I ran into the same error message. In my case, it was due to setting the backend to Jinja2. In my settings file:

TEMPLATES = [
{
    'BACKEND': 'django.template.backends.jinja2.Jinja2',
...

Changing this back to the default fixed the problem:

TEMPLATES = [
{
    'BACKEND': 'django.template.backends.django.DjangoTemplates',
...

Still not sure if there is a way to use the Jinja2 backend with rest_framework.


Django Rest Framework文件上传

问题:Django Rest Framework文件上传

我正在使用Django Rest Framework和AngularJs上传文件。我的视图文件如下所示:

class ProductList(APIView):
    authentication_classes = (authentication.TokenAuthentication,)
    def get(self,request):
        if request.user.is_authenticated(): 
            userCompanyId = request.user.get_profile().companyId
            products = Product.objects.filter(company = userCompanyId)
            serializer = ProductSerializer(products,many=True)
            return Response(serializer.data)

    def post(self,request):
        serializer = ProductSerializer(data=request.DATA, files=request.FILES)
        if serializer.is_valid():
            serializer.save()
            return Response(data=request.DATA)

由于post方法的最后一行应返回所有数据,因此我有几个问题:

  • 如何检查里面是否有东西request.FILES
  • 如何序列化文件字段?
  • 我应该如何使用解析器?

I am using Django Rest Framework and AngularJs to upload a file. My view file looks like this:

class ProductList(APIView):
    authentication_classes = (authentication.TokenAuthentication,)
    def get(self,request):
        if request.user.is_authenticated(): 
            userCompanyId = request.user.get_profile().companyId
            products = Product.objects.filter(company = userCompanyId)
            serializer = ProductSerializer(products,many=True)
            return Response(serializer.data)

    def post(self,request):
        serializer = ProductSerializer(data=request.DATA, files=request.FILES)
        if serializer.is_valid():
            serializer.save()
            return Response(data=request.DATA)

As the last line of post method should return all the data, I have several questions:

  • how to check if there is anything in request.FILES?
  • how to serialize file field?
  • how should I use parser?

回答 0

使用FileUploadParser,一切都在请求中。改用put方法,您会在docs中找到一个示例:)

class FileUploadView(views.APIView):
    parser_classes = (FileUploadParser,)

    def put(self, request, filename, format=None):
        file_obj = request.FILES['file']
        # do some stuff with uploaded file
        return Response(status=204)

Use the FileUploadParser, it’s all in the request. Use a put method instead, you’ll find an example in the docs :)

class FileUploadView(views.APIView):
    parser_classes = (FileUploadParser,)

    def put(self, request, filename, format=None):
        file_obj = request.FILES['file']
        # do some stuff with uploaded file
        return Response(status=204)

回答 1

我使用的是同一堆栈,也在寻找文件上传的示例,但是由于我使用ModelViewSet而不是APIView,因此情况更简单。原来的钥匙是pre_save钩子。我最终将其与angular-file-upload模块一起使用,如下所示:

# Django
class ExperimentViewSet(ModelViewSet):
    queryset = Experiment.objects.all()
    serializer_class = ExperimentSerializer

    def pre_save(self, obj):
        obj.samplesheet = self.request.FILES.get('file')

class Experiment(Model):
    notes = TextField(blank=True)
    samplesheet = FileField(blank=True, default='')
    user = ForeignKey(User, related_name='experiments')

class ExperimentSerializer(ModelSerializer):
    class Meta:
        model = Experiment
        fields = ('id', 'notes', 'samplesheet', 'user')

// AngularJS
controller('UploadExperimentCtrl', function($scope, $upload) {
    $scope.submit = function(files, exp) {
        $upload.upload({
            url: '/api/experiments/' + exp.id + '/',
            method: 'PUT',
            data: {user: exp.user.id},
            file: files[0]
        });
    };
});

I’m using the same stack and was also looking for an example of file upload, but my case is simpler since I use the ModelViewSet instead of APIView. The key turned out to be the pre_save hook. I ended up using it together with the angular-file-upload module like so:

# Django
class ExperimentViewSet(ModelViewSet):
    queryset = Experiment.objects.all()
    serializer_class = ExperimentSerializer

    def pre_save(self, obj):
        obj.samplesheet = self.request.FILES.get('file')

class Experiment(Model):
    notes = TextField(blank=True)
    samplesheet = FileField(blank=True, default='')
    user = ForeignKey(User, related_name='experiments')

class ExperimentSerializer(ModelSerializer):
    class Meta:
        model = Experiment
        fields = ('id', 'notes', 'samplesheet', 'user')

// AngularJS
controller('UploadExperimentCtrl', function($scope, $upload) {
    $scope.submit = function(files, exp) {
        $upload.upload({
            url: '/api/experiments/' + exp.id + '/',
            method: 'PUT',
            data: {user: exp.user.id},
            file: files[0]
        });
    };
});

回答 2

最后,我可以使用Django上传图片。这是我的工作代码

views.py

class FileUploadView(APIView):
    parser_classes = (FileUploadParser, )

    def post(self, request, format='jpg'):
        up_file = request.FILES['file']
        destination = open('/Users/Username/' + up_file.name, 'wb+')
        for chunk in up_file.chunks():
            destination.write(chunk)
        destination.close()  # File should be closed only after all chuns are added

        # ...
        # do some stuff with uploaded file
        # ...
        return Response(up_file.name, status.HTTP_201_CREATED)

urls.py

urlpatterns = patterns('', 
url(r'^imageUpload', views.FileUploadView.as_view())

卷曲请求上传

curl -X POST -S -H -u "admin:password" -F "file=@img.jpg;type=image/jpg" 127.0.0.1:8000/resourceurl/imageUpload

Finally I am able to upload image using Django. Here is my working code

views.py

class FileUploadView(APIView):
    parser_classes = (FileUploadParser, )

    def post(self, request, format='jpg'):
        up_file = request.FILES['file']
        destination = open('/Users/Username/' + up_file.name, 'wb+')
        for chunk in up_file.chunks():
            destination.write(chunk)
        destination.close()  # File should be closed only after all chuns are added

        # ...
        # do some stuff with uploaded file
        # ...
        return Response(up_file.name, status.HTTP_201_CREATED)

urls.py

urlpatterns = patterns('', 
url(r'^imageUpload', views.FileUploadView.as_view())

curl request to upload

curl -X POST -S -H -u "admin:password" -F "file=@img.jpg;type=image/jpg" 127.0.0.1:8000/resourceurl/imageUpload

回答 3

在此上花了1天后,我发现…

对于需要上载文件并发送一些数据的人,没有直接的前进方式可以使它工作。json api规范中有一个未解决的问题。一种可能性是我见过在使用multipart/related这里,但是我认为很难在drf中实现它。

最后,我实现的是将请求发送为formdata。您将每个文件作为文件发送,所有其他数据作为文本发送。现在,以文本形式发送数据有两种选择。情况1)您可以将每个数据作为键值对发送,或情况2)您可以有一个名为data的键,并将整个json作为值字符串发送。

如果您具有简单的字段,则第一种方法开箱即用,但如果嵌套了序列化,则将是一个问题。多部分解析器将无法解析嵌套字段。

下面我提供两种情况的实现

型号

class Posts(models.Model):
    id = models.UUIDField(default=uuid.uuid4, primary_key=True, editable=False)
    caption = models.TextField(max_length=1000)
    media = models.ImageField(blank=True, default="", upload_to="posts/")
    tags = models.ManyToManyField('Tags', related_name='posts')

serializers.py->无需特殊更改,由于可写的ManyToMany Field含义,此处没有显示我的序列化程序太长。

views.py

class PostsViewset(viewsets.ModelViewSet):
    serializer_class = PostsSerializer
    #parser_classes = (MultipartJsonParser, parsers.JSONParser) use this if you have simple key value pair as data with no nested serializers
    #parser_classes = (parsers.MultipartParser, parsers.JSONParser) use this if you want to parse json in the key value pair data sent
    queryset = Posts.objects.all()
    lookup_field = 'id'

现在,如果您遵循第一种方法,并且仅将非Json数据作为键值对发送,则不需要自定义解析器类。DRF的MultipartParser将完成这项工作。但是对于第二种情况,或者如果您有嵌套的序列化器(如我所示),则需要自定义解析器,如下所示。

utils.py

from django.http import QueryDict
import json
from rest_framework import parsers

class MultipartJsonParser(parsers.MultiPartParser):

    def parse(self, stream, media_type=None, parser_context=None):
        result = super().parse(
            stream,
            media_type=media_type,
            parser_context=parser_context
        )
        data = {}

        # for case1 with nested serializers
        # parse each field with json
        for key, value in result.data.items():
            if type(value) != str:
                data[key] = value
                continue
            if '{' in value or "[" in value:
                try:
                    data[key] = json.loads(value)
                except ValueError:
                    data[key] = value
            else:
                data[key] = value

        # for case 2
        # find the data field and parse it
        data = json.loads(result.data["data"])

        qdict = QueryDict('', mutable=True)
        qdict.update(data)
        return parsers.DataAndFiles(qdict, result.files)

该序列化程序将基本上解析值中的所有json内容。

邮递员在两种情况下的请求示例:情况1

情况二

After spending 1 day on this, I figured out that …

For someone who needs to upload a file and send some data, there is no straight fwd way you can get it to work. There is an open issue in json api specs for this. One possibility i have seen is to use multipart/related as shown here, but i think its very hard to implement it in drf.

Finally what i had implemented was to send the request as formdata. You would send each file as file and all other data as text. Now for sending the data as text you have two choices. case 1) you can send each data as key value pair or case 2) you can have a single key called data and send the whole json as string in value.

The first method would work out of the box if you have simple fields, but will be a issue if you have nested serializes. The multipart parser wont be able to parse the nested fields.

Below i am providing the implementation for both the cases

Models.py

class Posts(models.Model):
    id = models.UUIDField(default=uuid.uuid4, primary_key=True, editable=False)
    caption = models.TextField(max_length=1000)
    media = models.ImageField(blank=True, default="", upload_to="posts/")
    tags = models.ManyToManyField('Tags', related_name='posts')

serializers.py -> no special changes needed, not showing my serializer here as its too lengthy because of the writable ManyToMany Field implimentation.

views.py

class PostsViewset(viewsets.ModelViewSet):
    serializer_class = PostsSerializer
    #parser_classes = (MultipartJsonParser, parsers.JSONParser) use this if you have simple key value pair as data with no nested serializers
    #parser_classes = (parsers.MultipartParser, parsers.JSONParser) use this if you want to parse json in the key value pair data sent
    queryset = Posts.objects.all()
    lookup_field = 'id'

Now, if you are following the first method and is only sending non-Json data as key value pairs, you don’t need a custom parser class. DRF’d MultipartParser will do the job. But for the second case or if you have nested serializers (like i have shown) you will need custom parser as shown below.

utils.py

from django.http import QueryDict
import json
from rest_framework import parsers

class MultipartJsonParser(parsers.MultiPartParser):

    def parse(self, stream, media_type=None, parser_context=None):
        result = super().parse(
            stream,
            media_type=media_type,
            parser_context=parser_context
        )
        data = {}

        # for case1 with nested serializers
        # parse each field with json
        for key, value in result.data.items():
            if type(value) != str:
                data[key] = value
                continue
            if '{' in value or "[" in value:
                try:
                    data[key] = json.loads(value)
                except ValueError:
                    data[key] = value
            else:
                data[key] = value

        # for case 2
        # find the data field and parse it
        data = json.loads(result.data["data"])

        qdict = QueryDict('', mutable=True)
        qdict.update(data)
        return parsers.DataAndFiles(qdict, result.files)

This serializer would basically parse any json content in the values.

The request example in post man for both cases: case 1 ,

Case 2


回答 4

我用ModelViewSet和ModelSerializer解决了这个问题。希望这对社区有所帮助。

我还希望在序列化程序本身而不是视图中进行验证和Object-> JSON(反之亦然)登录。

让我们通过示例来了解它。

说,我想创建FileUploader API。它将在其中存储ID,file_path,file_name,大小,所有者等字段的位置。请参阅下面的示例模型:

class FileUploader(models.Model):
    file = models.FileField()
    name = models.CharField(max_length=100) #name is filename without extension
    version = models.IntegerField(default=0)
    upload_date = models.DateTimeField(auto_now=True, db_index=True)
    owner = models.ForeignKey('auth.User', related_name='uploaded_files')
    size = models.IntegerField(default=0)

现在,对于API,这就是我想要的:

  1. 得到:

当我触发GET端点时,我希望每个上传文件都具有以上所有字段。

  1. 开机自检:

但是对于用户创建/上传文件而言,为什么她不得不担心传递所有这些字段。她可以仅上传文件,然后,我想,序列化程序可以从上传的FILE中获取其余字段。

Searilizer: 问题:我在序列化器下面创建了我的目的。但是不确定它是否是实现它的正确方法。

class FileUploaderSerializer(serializers.ModelSerializer):
    # overwrite = serializers.BooleanField()
    class Meta:
        model = FileUploader
        fields = ('file','name','version','upload_date', 'size')
        read_only_fields = ('name','version','owner','upload_date', 'size')

   def validate(self, validated_data):
        validated_data['owner'] = self.context['request'].user
        validated_data['name'] = os.path.splitext(validated_data['file'].name)[0]
        validated_data['size'] = validated_data['file'].size
        #other validation logic
        return validated_data

    def create(self, validated_data):
        return FileUploader.objects.create(**validated_data)

参考视图集:

class FileUploaderViewSet(viewsets.ModelViewSet):
    serializer_class = FileUploaderSerializer
    parser_classes = (MultiPartParser, FormParser,)

    # overriding default query set
    queryset = LayerFile.objects.all()

    def get_queryset(self, *args, **kwargs):
        qs = super(FileUploaderViewSet, self).get_queryset(*args, **kwargs)
        qs = qs.filter(owner=self.request.user)
        return qs

I solved this problem with ModelViewSet and ModelSerializer. Hope this will help community.

I also preffer to have validation and Object->JSON (and vice-versa) login in serializer itself rather than in views.

Lets understand it by example.

Say, I want to create FileUploader API. Where it will be storing fields like id, file_path, file_name, size, owner etc in database. See sample model below:

class FileUploader(models.Model):
    file = models.FileField()
    name = models.CharField(max_length=100) #name is filename without extension
    version = models.IntegerField(default=0)
    upload_date = models.DateTimeField(auto_now=True, db_index=True)
    owner = models.ForeignKey('auth.User', related_name='uploaded_files')
    size = models.IntegerField(default=0)

Now, For APIs this is what I want:

  1. GET:

When I fire the GET endpoint, I want all above fields for every uploaded file.

  1. POST:

But for user to create/upload file, why she has to worry about passing all these fields. She can just upload the file and then, I suppose, serializer can get rest of the fields from uploaded FILE.

Searilizer: Question: I created below serializer to serve my purpose. But not sure if its the right way to implement it.

class FileUploaderSerializer(serializers.ModelSerializer):
    # overwrite = serializers.BooleanField()
    class Meta:
        model = FileUploader
        fields = ('file','name','version','upload_date', 'size')
        read_only_fields = ('name','version','owner','upload_date', 'size')

   def validate(self, validated_data):
        validated_data['owner'] = self.context['request'].user
        validated_data['name'] = os.path.splitext(validated_data['file'].name)[0]
        validated_data['size'] = validated_data['file'].size
        #other validation logic
        return validated_data

    def create(self, validated_data):
        return FileUploader.objects.create(**validated_data)

Viewset for reference:

class FileUploaderViewSet(viewsets.ModelViewSet):
    serializer_class = FileUploaderSerializer
    parser_classes = (MultiPartParser, FormParser,)

    # overriding default query set
    queryset = LayerFile.objects.all()

    def get_queryset(self, *args, **kwargs):
        qs = super(FileUploaderViewSet, self).get_queryset(*args, **kwargs)
        qs = qs.filter(owner=self.request.user)
        return qs

回答 5

根据我的经验,您不需要对文件字段做任何特别的事情,只需告诉它利用文件字段即可:

from rest_framework import routers, serializers, viewsets

class Photo(django.db.models.Model):
    file = django.db.models.ImageField()

    def __str__(self):
        return self.file.name

class PhotoSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Photo
        fields = ('id', 'file')   # <-- HERE

class PhotoViewSet(viewsets.ModelViewSet):
    queryset = models.Photo.objects.all()
    serializer_class = PhotoSerializer

router = routers.DefaultRouter()
router.register(r'photos', PhotoViewSet)

api_urlpatterns = ([
    url('', include(router.urls)),
], 'api')
urlpatterns += [
    url(r'^api/', include(api_urlpatterns)),
]

您就可以上传文件了:

curl -sS http://example.com/api/photos/ -F 'file=@/path/to/file'

-F field=value为模型具有的每个其他字段添加。并且不要忘记添加身份验证。

From my experience, you don’t need to do anything particular about file fields, you just tell it to make use of the file field:

from rest_framework import routers, serializers, viewsets

class Photo(django.db.models.Model):
    file = django.db.models.ImageField()

    def __str__(self):
        return self.file.name

class PhotoSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Photo
        fields = ('id', 'file')   # <-- HERE

class PhotoViewSet(viewsets.ModelViewSet):
    queryset = models.Photo.objects.all()
    serializer_class = PhotoSerializer

router = routers.DefaultRouter()
router.register(r'photos', PhotoViewSet)

api_urlpatterns = ([
    url('', include(router.urls)),
], 'api')
urlpatterns += [
    url(r'^api/', include(api_urlpatterns)),
]

and you’re ready to upload files:

curl -sS http://example.com/api/photos/ -F 'file=@/path/to/file'

Add -F field=value for each extra field your model has. And don’t forget to add authentication.


回答 6

如果有人对ModelViewset for Django Rest Framework最简单的示例感兴趣。

该模型是

class MyModel(models.Model):
    name = models.CharField(db_column='name', max_length=200, blank=False, null=False, unique=True)
    imageUrl = models.FileField(db_column='image_url', blank=True, null=True, upload_to='images/')

    class Meta:
        managed = True
        db_table = 'MyModel'

序列化器

class MyModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = MyModel
        fields = "__all__"

观点是,

class MyModelView(viewsets.ModelViewSet):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer

在邮递员中测试

If anyone interested in the easiest example with ModelViewset for Django Rest Framework.

The Model is,

class MyModel(models.Model):
    name = models.CharField(db_column='name', max_length=200, blank=False, null=False, unique=True)
    imageUrl = models.FileField(db_column='image_url', blank=True, null=True, upload_to='images/')

    class Meta:
        managed = True
        db_table = 'MyModel'

The Serializer,

class MyModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = MyModel
        fields = "__all__"

And the View is,

class MyModelView(viewsets.ModelViewSet):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer

Test in Postman,


回答 7

在django-rest-framework中,请求数据由解析Parsers
http://www.django-rest-framework.org/api-guide/parsers/

默认情况下,django-rest-framework采用解析器类JSONParser。它将数据解析为json。因此,文件将不会被解析。
如果我们希望文件与其他数据一起被解析,则应使用以下解析器类之一。

FormParser
MultiPartParser
FileUploadParser

In django-rest-framework request data is parsed by the Parsers.
http://www.django-rest-framework.org/api-guide/parsers/

By default django-rest-framework takes parser class JSONParser. It will parse the data into json. so, files will not be parsed with it.
If we want files to be parsed along with other data we should use one of the below parser classes.

FormParser
MultiPartParser
FileUploadParser

回答 8

    from rest_framework import status
    from rest_framework.response import Response
    class FileUpload(APIView):
         def put(request):
             try:
                file = request.FILES['filename']
                #now upload to s3 bucket or your media file
             except Exception as e:
                   print e
                   return Response(status, 
                           status.HTTP_500_INTERNAL_SERVER_ERROR)
             return Response(status, status.HTTP_200_OK)
    from rest_framework import status
    from rest_framework.response import Response
    class FileUpload(APIView):
         def put(request):
             try:
                file = request.FILES['filename']
                #now upload to s3 bucket or your media file
             except Exception as e:
                   print e
                   return Response(status, 
                           status.HTTP_500_INTERNAL_SERVER_ERROR)
             return Response(status, status.HTTP_200_OK)

回答 9

def post(self,request):
        serializer = ProductSerializer(data=request.DATA, files=request.FILES)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
def post(self,request):
        serializer = ProductSerializer(data=request.DATA, files=request.FILES)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)

回答 10

我想写一个我觉得更干净,更易于维护的选项。我们将使用defaultRouter为我们的视图集添加CRUD网址,并且还将添加一个固定的网址,以指定同一视图集内的上载器视图。

**** views.py 

from rest_framework import viewsets, serializers
from rest_framework.decorators import action, parser_classes
from rest_framework.parsers import JSONParser, MultiPartParser
from rest_framework.response import Response
from rest_framework_csv.parsers import CSVParser
from posts.models import Post
from posts.serializers import PostSerializer     


class PostsViewSet(viewsets.ModelViewSet):

    queryset = Post.objects.all()
    serializer_class = PostSerializer 
    parser_classes = (JSONParser, MultiPartParser, CSVParser)


    @action(detail=False, methods=['put'], name='Uploader View', parser_classes=[CSVParser],)
    def uploader(self, request, filename, format=None):
        # Parsed data will be returned within the request object by accessing 'data' attr  
        _data = request.data

        return Response(status=204)

项目的主要urls.py

**** urls.py 

from rest_framework import routers
from posts.views import PostsViewSet


router = routers.DefaultRouter()
router.register(r'posts', PostsViewSet)

urlpatterns = [
    url(r'^posts/uploader/(?P<filename>[^/]+)$', PostsViewSet.as_view({'put': 'uploader'}), name='posts_uploader')
    url(r'^', include(router.urls), name='root-api'),
    url('admin/', admin.site.urls),
]

.-自述文件。

当我们将@action装饰器添加到类方法’uploader’时,魔术发生了。通过指定“ methods = [‘put’]”参数,我们仅允许PUT请求;非常适合文件上传。

我还添加了参数“ parser_classes”,以显示可以选择将解析内容的解析器。我从rest_framework_csv包中添加了CSVParser,以演示如果需要此功能时我们如何仅接受某些类型的文件,在我的情况下,我仅接受“ Content-Type:text / csv”。注意:如果要添加自定义解析器,则需要在ViewSet的parsers_classes中指定它们,因为在访问上载器方法解析器之前,请求会将允许的media_type与主(类)解析器进行比较。

现在我们需要告诉Django如何使用此方法,以及可以在我们的url中实现的位置。那就是我们添加固定网址的时候(简单目的)。该网址将带有“文件名”参数,该参数稍后将在方法中传递。我们需要传递此方法“ uploader”,并在列表中指定http协议(’PUT’)到PostsViewSet.as_view方法。

当我们进入以下网址时

 http://example.com/posts/uploader/ 

它将期望一个带有标头的PUT请求,该标头指定“ Content-Type”和Content-Disposition:附件;filename =“ something.csv”。

curl -v -u user:pass http://example.com/posts/uploader/ --upload-file ./something.csv --header "Content-type:text/csv"

I’d like to write another option that I feel is cleaner and easier to maintain. We’ll be using the defaultRouter to add CRUD urls for our viewset and we’ll add one more fixed url specifying the uploader view within the same viewset.

**** views.py 

from rest_framework import viewsets, serializers
from rest_framework.decorators import action, parser_classes
from rest_framework.parsers import JSONParser, MultiPartParser
from rest_framework.response import Response
from rest_framework_csv.parsers import CSVParser
from posts.models import Post
from posts.serializers import PostSerializer     


class PostsViewSet(viewsets.ModelViewSet):

    queryset = Post.objects.all()
    serializer_class = PostSerializer 
    parser_classes = (JSONParser, MultiPartParser, CSVParser)


    @action(detail=False, methods=['put'], name='Uploader View', parser_classes=[CSVParser],)
    def uploader(self, request, filename, format=None):
        # Parsed data will be returned within the request object by accessing 'data' attr  
        _data = request.data

        return Response(status=204)

Project’s main urls.py

**** urls.py 

from rest_framework import routers
from posts.views import PostsViewSet


router = routers.DefaultRouter()
router.register(r'posts', PostsViewSet)

urlpatterns = [
    url(r'^posts/uploader/(?P<filename>[^/]+)$', PostsViewSet.as_view({'put': 'uploader'}), name='posts_uploader')
    url(r'^', include(router.urls), name='root-api'),
    url('admin/', admin.site.urls),
]

.- README.

The magic happens when we add @action decorator to our class method ‘uploader’. By specifying “methods=[‘put’]” argument, we are only allowing PUT requests; perfect for file uploading.

I also added the argument “parser_classes” to show you can select the parser that will parse your content. I added CSVParser from the rest_framework_csv package, to demonstrate how we can accept only certain type of files if this functionality is required, in my case I’m only accepting “Content-Type: text/csv”. Note: If you’re adding custom Parsers, you’ll need to specify them in parsers_classes in the ViewSet due the request will compare the allowed media_type with main (class) parsers before accessing the uploader method parsers.

Now we need to tell Django how to go to this method and where can be implemented in our urls. That’s when we add the fixed url (Simple purposes). This Url will take a “filename” argument that will be passed in the method later on. We need to pass this method “uploader”, specifying the http protocol (‘PUT’) in a list to the PostsViewSet.as_view method.

When we land in the following url

 http://example.com/posts/uploader/ 

it will expect a PUT request with headers specifying “Content-Type” and Content-Disposition: attachment; filename=”something.csv”.

curl -v -u user:pass http://example.com/posts/uploader/ --upload-file ./something.csv --header "Content-type:text/csv"

回答 11

这是我已应用的一种方法,希望会对您有所帮助。

     class Model_File_update(APIView):
         parser_classes = (MultiPartParser, FormParser)
         permission_classes = [IsAuthenticated]  # it will check if the user is authenticated or not
         authentication_classes = [JSONWebTokenAuthentication]  # it will authenticate the person by JSON web token

         def put(self, request):
            id = request.GET.get('id')
            obj = Model.objects.get(id=id)
            serializer = Model_Upload_Serializer(obj, data=request.data)
            if serializer.is_valid():
               serializer.save()
               return Response(serializer.data, status=200)
            else:
               return Response(serializer.errors, status=400)

This is the one of the approach I’ve applied hopefully it’ll help.

     class Model_File_update(APIView):
         parser_classes = (MultiPartParser, FormParser)
         permission_classes = [IsAuthenticated]  # it will check if the user is authenticated or not
         authentication_classes = [JSONWebTokenAuthentication]  # it will authenticate the person by JSON web token

         def put(self, request):
            id = request.GET.get('id')
            obj = Model.objects.get(id=id)
            serializer = Model_Upload_Serializer(obj, data=request.data)
            if serializer.is_valid():
               serializer.save()
               return Response(serializer.data, status=200)
            else:
               return Response(serializer.errors, status=400)

回答 12

您可以通过生成解析器类来解析特定字段,然后将它们直接馈入标准DRF序列化器中,来概括@Nithin的答案以直接与DRF的现有序列化器系统一起工作:

from django.http import QueryDict
import json
from rest_framework import parsers


def gen_MultipartJsonParser(json_fields):
    class MultipartJsonParser(parsers.MultiPartParser):

        def parse(self, stream, media_type=None, parser_context=None):
            result = super().parse(
                stream,
                media_type=media_type,
                parser_context=parser_context
            )
            data = {}
            # find the data field and parse it
            qdict = QueryDict('', mutable=True)
            for json_field in json_fields:
                json_data = result.data.get(json_field, None)
                if not json_data:
                    continue
                data = json.loads(json_data)
                if type(data) == list:
                    for d in data:
                        qdict.update({json_field: d})
                else:
                    qdict.update({json_field: data})

            return parsers.DataAndFiles(qdict, result.files)

    return MultipartJsonParser

用法如下:

class MyFileViewSet(ModelViewSet):
    parser_classes = [gen_MultipartJsonParser(['tags', 'permissions'])]
    #                                           ^^^^^^^^^^^^^^^^^^^
    #                              Fields that need to be further JSON parsed
    ....

You can generalize @Nithin’s answer to work directly with DRF’s existing serializer system by generating a parser class to parse specific fields which are then fed directly into the standard DRF serializers:

from django.http import QueryDict
import json
from rest_framework import parsers


def gen_MultipartJsonParser(json_fields):
    class MultipartJsonParser(parsers.MultiPartParser):

        def parse(self, stream, media_type=None, parser_context=None):
            result = super().parse(
                stream,
                media_type=media_type,
                parser_context=parser_context
            )
            data = {}
            # find the data field and parse it
            qdict = QueryDict('', mutable=True)
            for json_field in json_fields:
                json_data = result.data.get(json_field, None)
                if not json_data:
                    continue
                data = json.loads(json_data)
                if type(data) == list:
                    for d in data:
                        qdict.update({json_field: d})
                else:
                    qdict.update({json_field: data})

            return parsers.DataAndFiles(qdict, result.files)

    return MultipartJsonParser

This is used like:

class MyFileViewSet(ModelViewSet):
    parser_classes = [gen_MultipartJsonParser(['tags', 'permissions'])]
    #                                           ^^^^^^^^^^^^^^^^^^^
    #                              Fields that need to be further JSON parsed
    ....

回答 13

如果您正在使用ModelViewSet,那么实际上您已经做好了!它为您处理一切!您只需要将该字段放入ModelSerializer中并content-type=multipart/form-data;在客户端中进行设置即可。

但您知道您不能以json格式发送文件。(在客户端中将content-type设置为application / json时)。除非您使用Base64格式。

因此,您有两种选择:

  • ModelViewSetModelSerializer处理工作,并使用发送请求content-type=multipart/form-data;
  • 将字段设置ModelSerializer为,Base64ImageField (or) Base64FileField并告诉您的客户端将文件编码为Base64并设置content-type=application/json

If you are using ModelViewSet, well actually you are done! It handles every things for you! You just need to put the field in your ModelSerializer and set content-type=multipart/form-data; in your client.

BUT as you know you can not send files in json format. (when content-type is set to application/json in your client). Unless you use Base64 format.

So you have two choices:

  • let ModelViewSet and ModelSerializer handle the job and send the request using content-type=multipart/form-data;
  • set the field in ModelSerializer as Base64ImageField (or) Base64FileField and tell your client to encode the file to Base64 and set the content-type=application/json

回答 14

models.py

from django.db import models

import uuid

class File(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    file = models.FileField(blank=False, null=False)
    
    def __str__(self):
        return self.file.name

serializers.py

from rest_framework import serializers
from .models import File

class FileSerializer(serializers.ModelSerializer):
    class Meta:
        model = File
        fields = "__all__"

views.py

from django.shortcuts import render
from rest_framework.parsers import FileUploadParser
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import status

from .serializers import FileSerializer


class FileUploadView(APIView):
    permission_classes = []
    parser_class = (FileUploadParser,)

    def post(self, request, *args, **kwargs):

      file_serializer = FileSerializer(data=request.data)

      if file_serializer.is_valid():
          file_serializer.save()
          return Response(file_serializer.data, status=status.HTTP_201_CREATED)
      else:
          return Response(file_serializer.errors, status=status.HTTP_400_BAD_REQUEST)

urls.py

from apps.files import views as FileViews

urlpatterns = [
    path('api/files', FileViews.FileUploadView.as_view()),
]

settings.py

# file uload parameters
MEDIA_URL =  '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

发送一个后发请求,api/files并将您的文件附加到form-data字段中file。该文件将被上载到/media文件夹,并且数据库记录将添加ID和文件名。

models.py

from django.db import models

import uuid

class File(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    file = models.FileField(blank=False, null=False)
    
    def __str__(self):
        return self.file.name

serializers.py

from rest_framework import serializers
from .models import File

class FileSerializer(serializers.ModelSerializer):
    class Meta:
        model = File
        fields = "__all__"

views.py

from django.shortcuts import render
from rest_framework.parsers import FileUploadParser
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import status

from .serializers import FileSerializer


class FileUploadView(APIView):
    permission_classes = []
    parser_class = (FileUploadParser,)

    def post(self, request, *args, **kwargs):

      file_serializer = FileSerializer(data=request.data)

      if file_serializer.is_valid():
          file_serializer.save()
          return Response(file_serializer.data, status=status.HTTP_201_CREATED)
      else:
          return Response(file_serializer.errors, status=status.HTTP_400_BAD_REQUEST)

urls.py

from apps.files import views as FileViews

urlpatterns = [
    path('api/files', FileViews.FileUploadView.as_view()),
]

settings.py

# file uload parameters
MEDIA_URL =  '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

Send a post request to api/files with a your file attached to a form-data field file. The file will be uploaded to /media folder and a db record will be added with id and file name.


Django Rest Framework-未提供身份验证凭据

问题:Django Rest Framework-未提供身份验证凭据

我正在使用Django Rest Framework开发API。我试图列出或创建“订单”对象,但是当我尝试访问控制台时,出现此错误:

{"detail": "Authentication credentials were not provided."}

观看次数:

from django.shortcuts import render
from rest_framework import viewsets
from django.contrib.auth.models import User
from rest_framework.renderers import JSONRenderer, YAMLRenderer
from rest_framework.response import Response
from rest_framework.views import APIView
from order.models import *
from API.serializers import *
from rest_framework.permissions import IsAuthenticated

class OrderViewSet(viewsets.ModelViewSet):
    model = Order
    serializer_class = OrderSerializer
    permission_classes = (IsAuthenticated,)

序列化器:

class OrderSerializer(serializers.HyperlinkedModelSerializer):

    class Meta:
        model = Order
        fields = ('field1', 'field2')

我的网址:

# -*- coding: utf-8 -*-
from django.conf.urls import patterns, include, url
from django.conf import settings
from django.contrib import admin
from django.utils.functional import curry
from django.views.defaults import *
from rest_framework import routers
from API.views import *

admin.autodiscover()

handler500 = "web.views.server_error"
handler404 = "web.views.page_not_found_error"

router = routers.DefaultRouter()
router.register(r'orders', OrdersViewSet)

urlpatterns = patterns('',
    url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
    url(r'^api-token-auth/', 'rest_framework.authtoken.views.obtain_auth_token'),
    url(r'^api/', include(router.urls)),
)

然后我在控制台中使用以下命令:

curl -X GET http://127.0.0.1:8000/api/orders/ -H 'Authorization: Token 12383dcb52d627eabd39e7e88501e96a2sadc55'

错误说:

{"detail": "Authentication credentials were not provided."}

I’m developing an API using Django Rest Framework. I’m trying to list or create an “Order” object, but when i’m trying to access the console gives me this error:

{"detail": "Authentication credentials were not provided."}

Views:

from django.shortcuts import render
from rest_framework import viewsets
from django.contrib.auth.models import User
from rest_framework.renderers import JSONRenderer, YAMLRenderer
from rest_framework.response import Response
from rest_framework.views import APIView
from order.models import *
from API.serializers import *
from rest_framework.permissions import IsAuthenticated

class OrderViewSet(viewsets.ModelViewSet):
    model = Order
    serializer_class = OrderSerializer
    permission_classes = (IsAuthenticated,)

Serializer:

class OrderSerializer(serializers.HyperlinkedModelSerializer):

    class Meta:
        model = Order
        fields = ('field1', 'field2')

And my URLs:

# -*- coding: utf-8 -*-
from django.conf.urls import patterns, include, url
from django.conf import settings
from django.contrib import admin
from django.utils.functional import curry
from django.views.defaults import *
from rest_framework import routers
from API.views import *

admin.autodiscover()

handler500 = "web.views.server_error"
handler404 = "web.views.page_not_found_error"

router = routers.DefaultRouter()
router.register(r'orders', OrdersViewSet)

urlpatterns = patterns('',
    url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
    url(r'^api-token-auth/', 'rest_framework.authtoken.views.obtain_auth_token'),
    url(r'^api/', include(router.urls)),
)

And then I’m using this command in the console:

curl -X GET http://127.0.0.1:8000/api/orders/ -H 'Authorization: Token 12383dcb52d627eabd39e7e88501e96a2sadc55'

And the error say:

{"detail": "Authentication credentials were not provided."}

回答 0

如果您使用mod_wsgi在Apache上运行Django,则必须添加

WSGIPassAuthorization On

在您的httpd.conf中。否则,授权标头将被mod_wsgi剥离。

If you are runnig Django on Apache using mod_wsgi you have to add

WSGIPassAuthorization On

in your httpd.conf. Otherwise authorization header will be stripped out by mod_wsgi.


回答 1

通过在我的settings.py中添加“ DEFAULT_AUTHENTICATION_CLASSES”来解决

REST_FRAMEWORK = {
   'DEFAULT_AUTHENTICATION_CLASSES': (
       'rest_framework.authentication.TokenAuthentication',
   ),
   'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAdminUser'
   ),
}

Solved by adding “DEFAULT_AUTHENTICATION_CLASSES” to my settings.py

REST_FRAMEWORK = {
   'DEFAULT_AUTHENTICATION_CLASSES': (
       'rest_framework.authentication.TokenAuthentication',
   ),
   'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAdminUser'
   ),
}

回答 2

这可以帮助我在settings.py中没有“ DEFAULT_PERMISSION_CLASSES”的情况

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.TokenAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    ),
    'PAGE_SIZE': 10
}

This help me out without “DEFAULT_PERMISSION_CLASSES” in my settings.py

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.TokenAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    ),
    'PAGE_SIZE': 10
}

回答 3

仅对于其他人,由于同样的错误而以同样的错误登陆,如果您request.user是您AnonymousUser的用户,而不是实际有权访问URL的正确用户,则可能会出现此问题。您可以通过打印的值看到这一点request.user。如果确实是匿名用户,这些步骤可能会有所帮助:

  1. 请确保您有 'rest_framework.authtoken'INSTALLED_APPS你的settings.py

  2. 确保您在settings.py以下位置有此商品:

    REST_FRAMEWORK = {
    
        'DEFAULT_AUTHENTICATION_CLASSES': (
            'rest_framework.authentication.TokenAuthentication',
            # ...
        ),
    
        # ...
    }
  3. 确保您具有登录用户的正确令牌。如果没有令牌,请在此处了解如何获取令牌。基本上,POST如果您提供正确的用户名和密码,则需要对视图进行请求,该视图将为您提供令牌。例:

    curl -X POST -d "user=Pepe&password=aaaa"  http://localhost:8000/
  4. 确保您尝试访问的视图具有以下内容:

    class some_fancy_example_view(ModelViewSet): 
    """
    not compulsary it has to be 'ModelViewSet' this can be anything like APIview etc, depending on your requirements.
    """
        permission_classes = (IsAuthenticated,) 
        authentication_classes = (TokenAuthentication,) 
        # ...
  5. curl现在以这种方式使用:

    curl -X (your_request_method) -H  "Authorization: Token <your_token>" <your_url>

例:

    curl -X GET http://127.0.0.1:8001/expenses/  -H "Authorization: Token 9463b437afdd3f34b8ec66acda4b192a815a15a8"

Just for other people landing up here with same error, this issue can arise if your request.user is AnonymousUser and not the right user who is actually authorized to access the URL. You can see that by printing value of request.user . If it is indeed an anonymous user, these steps might help:

  1. Make sure you have 'rest_framework.authtoken' in INSTALLED_APPS in your settings.py.

  2. Make sure you have this somewhere in settings.py:

    REST_FRAMEWORK = {
    
        'DEFAULT_AUTHENTICATION_CLASSES': (
            'rest_framework.authentication.TokenAuthentication',
            # ...
        ),
    
        # ...
    }
    
  3. Make sure you have the correct token for the user who is logged in. If you do not have the token, learn how to get it here. Basically, you need to do a POST request to a view which gives you the token if you provide the correct username and password. Example:

    curl -X POST -d "user=Pepe&password=aaaa"  http://localhost:8000/
    
  4. Make sure the view which you are trying to access, has these:

    class some_fancy_example_view(ModelViewSet): 
    """
    not compulsary it has to be 'ModelViewSet' this can be anything like APIview etc, depending on your requirements.
    """
        permission_classes = (IsAuthenticated,) 
        authentication_classes = (TokenAuthentication,) 
        # ...
    
  5. Use curl now this way:

    curl -X (your_request_method) -H  "Authorization: Token <your_token>" <your_url>
    

Example:

    curl -X GET http://127.0.0.1:8001/expenses/  -H "Authorization: Token 9463b437afdd3f34b8ec66acda4b192a815a15a8"

回答 4

如果您在命令行中玩耍(使用curl或HTTPie等),则可以使用BasicAuthentication测试/使用您的API

    REST_FRAMEWORK = {
        'DEFAULT_PERMISSION_CLASSES': [
            'rest_framework.permissions.IsAuthenticated',
        ],
        'DEFAULT_AUTHENTICATION_CLASSES': (
            'rest_framework.authentication.BasicAuthentication',  # enables simple command line authentication
            'rest_framework.authentication.SessionAuthentication',
            'rest_framework.authentication.TokenAuthentication',
        )
    }

然后可以使用curl

curl --user user:password -X POST http://example.com/path/ --data "some_field=some data"

httpie(在眼睛上更容易):

http -a user:password POST http://example.com/path/ some_field="some data"

或其他类似Advanced Rest Client(ARC)的东西

If you are playing around in the command line (using curl, or HTTPie etc) you can use BasicAuthentication to test/user your API

    REST_FRAMEWORK = {
        'DEFAULT_PERMISSION_CLASSES': [
            'rest_framework.permissions.IsAuthenticated',
        ],
        'DEFAULT_AUTHENTICATION_CLASSES': (
            'rest_framework.authentication.BasicAuthentication',  # enables simple command line authentication
            'rest_framework.authentication.SessionAuthentication',
            'rest_framework.authentication.TokenAuthentication',
        )
    }

You can then use curl

curl --user user:password -X POST http://example.com/path/ --data "some_field=some data"

or httpie (its easier on the eyes):

http -a user:password POST http://example.com/path/ some_field="some data"

or something else like Advanced Rest Client (ARC)


回答 5

我也遇到了同样的事情,因为我错过了添加

authentication_classes =(令牌认证)

在我的API视图类中。

class ServiceList(generics.ListCreateAPIView):
    authentication_classes = (SessionAuthentication, BasicAuthentication, TokenAuthentication)
    queryset = Service.objects.all()
    serializer_class = ServiceSerializer
    permission_classes = (IsAdminOrReadOnly,)

除了上述内容外,我们还需要在settings.py文件中明确告知Django 身份验证

REST_FRAMEWORK = {
   'DEFAULT_AUTHENTICATION_CLASSES': (
   'rest_framework.authentication.TokenAuthentication',
   )
}

I too faced the same since I missed adding

authentication_classes = (TokenAuthentication)

in my API view class.

class ServiceList(generics.ListCreateAPIView):
    authentication_classes = (SessionAuthentication, BasicAuthentication, TokenAuthentication)
    queryset = Service.objects.all()
    serializer_class = ServiceSerializer
    permission_classes = (IsAdminOrReadOnly,)

In addition to the above, we need to explicitly tell Django about the Authentication in settings.py file.

REST_FRAMEWORK = {
   'DEFAULT_AUTHENTICATION_CLASSES': (
   'rest_framework.authentication.TokenAuthentication',
   )
}

回答 6

在settings.py中添加SessionAuthentication就可以了

REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.SessionAuthentication', ), }

Adding SessionAuthentication in settings.py will do the job

REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.SessionAuthentication', ), }


回答 7

由于它是会话登录,因此您需要提供凭据,因此可以先进行 127.0.0:8000/admin 管理员登录,然后再正常运行

Since it is session Login so you need to provide you credentials so do 127.0.0:8000/admin admin and login later it will work fine


回答 8

对我来说,我必须在Django DRF上的Authorization标头前面加上“ JWT”,而不是“ Bearer”或“ Token”。然后它开始工作。例如-

Authorization: JWT asdflkj2ewmnsasdfmnwelfkjsdfghdfghdv.wlsfdkwefojdfgh

For me, I had to prepend my Authorization header with “JWT” instead of “Bearer” or “Token” on Django DRF. Then it started working. eg –

Authorization: JWT asdflkj2ewmnsasdfmnwelfkjsdfghdfghdv.wlsfdkwefojdfgh


回答 9

如果你正在使用authentication_classes,那么你应该有is_active作为TrueUser模型,这可能是False默认。

If you are using authentication_classes then you should have is_active as True in User model, which might be False by default.


在Django Rest Framework的响应中包括中介(通过模型)

问题:在Django Rest Framework的响应中包括中介(通过模型)

我有一个关于通过模型处理m2m /及其在django rest框架中的演示的问题。让我们举一个经典的例子:

models.py:

from django.db import models

class Member(models.Model):
    name = models.CharField(max_length = 20)
    groups = models.ManyToManyField('Group', through = 'Membership')

class Group(models.Model):
    name = models.CharField(max_length = 20)

class Membership(models.Model):
    member = models.ForeignKey('Member')
    group = models.ForeignKey('Group')
    join_date = models.DateTimeField()

serializers.py:

imports...

class MemberSerializer(ModelSerializer):
    class Meta:
        model = Member

class GroupSerializer(ModelSerializer):
    class Meta:
        model = Group

views.py:

imports...

class MemberViewSet(ModelViewSet):
    queryset = Member.objects.all()
    serializer_class = MemberSerializer

class GroupViewSet(ModelViewSet):
    queryset = Group.objects.all()
    serializer_class = GroupSerializer

在获取Member的实例时,我成功地接收了成员的所有字段以及它的组-但是,我仅获得组的详细信息,而没有来自Membership模型的其他详细信息。

换句话说,我希望收到:

{
   'id' : 2,
   'name' : 'some member',
   'groups' : [
      {
         'id' : 55,
         'name' : 'group 1'
         'join_date' : 34151564
      },
      {
         'id' : 56,
         'name' : 'group 2'
         'join_date' : 11200299
      }
   ]
}

注意join_date

我已经尝试了很多解决方案,当然包括有关它的Django Rest-Framework官方页面,而且似乎没有人给出正确的简单答案-我需要怎么做才能包括这些额外的字段?我发现使用django-tastypie更加简单明了,但是还有其他一些问题,并且更喜欢休息框架。

I have a question about dealing with m2m / through models and their presentation in django rest framework. Let’s take a classic example:

models.py:

from django.db import models

class Member(models.Model):
    name = models.CharField(max_length = 20)
    groups = models.ManyToManyField('Group', through = 'Membership')

class Group(models.Model):
    name = models.CharField(max_length = 20)

class Membership(models.Model):
    member = models.ForeignKey('Member')
    group = models.ForeignKey('Group')
    join_date = models.DateTimeField()

serializers.py:

imports...

class MemberSerializer(ModelSerializer):
    class Meta:
        model = Member

class GroupSerializer(ModelSerializer):
    class Meta:
        model = Group

views.py:

imports...

class MemberViewSet(ModelViewSet):
    queryset = Member.objects.all()
    serializer_class = MemberSerializer

class GroupViewSet(ModelViewSet):
    queryset = Group.objects.all()
    serializer_class = GroupSerializer

When GETing an instance of Member, I successfully receive all of the member’s fields and also its groups – however I only get the groups’ details, without extra details that comes from the Membership model.

In other words I expect to receive:

{
   'id' : 2,
   'name' : 'some member',
   'groups' : [
      {
         'id' : 55,
         'name' : 'group 1'
         'join_date' : 34151564
      },
      {
         'id' : 56,
         'name' : 'group 2'
         'join_date' : 11200299
      }
   ]
}

Note the join_date.

I have tried oh so many solutions, including of course Django Rest-Framework official page about it and no one seems to give a proper plain answer about it – what do I need to do to include these extra fields? I found it more straight-forward with django-tastypie but had some other problems and prefer rest-framework.


回答 0

怎么样…..

在您的MemberSerializer上,在其上定义一个字段,如下所示:

groups = MembershipSerializer(source='membership_set', many=True)

然后在会员序列化器上可以创建以下代码:

class MembershipSerializer(serializers.HyperlinkedModelSerializer):

    id = serializers.Field(source='group.id')
    name = serializers.Field(source='group.name')

    class Meta:
        model = Membership

        fields = ('id', 'name', 'join_date', )

这具有创建序列化值,组的总体效果,该序列化组具有所需的成员身份作为源,然后使用自定义序列化程序提取要显示的位。

编辑:由@bryanph评论,在DRF 3.0中serializers.field被重命名为serializers.ReadOnlyField,因此应显示为:

class MembershipSerializer(serializers.HyperlinkedModelSerializer):

    id = serializers.ReadOnlyField(source='group.id')
    name = serializers.ReadOnlyField(source='group.name')

    class Meta:
        model = Membership

        fields = ('id', 'name', 'join_date', )

适用于任何现代实施

How about…..

On your MemberSerializer, define a field on it like:

groups = MembershipSerializer(source='membership_set', many=True)

and then on your membership serializer you can create this:

class MembershipSerializer(serializers.HyperlinkedModelSerializer):

    id = serializers.Field(source='group.id')
    name = serializers.Field(source='group.name')

    class Meta:
        model = Membership

        fields = ('id', 'name', 'join_date', )

That has the overall effect of creating a serialized value, groups, that has as its source the membership you want, and then it uses a custom serializer to pull out the bits you want to display.

EDIT: as commented by @bryanph, serializers.field was renamed to serializers.ReadOnlyField in DRF 3.0, so this should read:

class MembershipSerializer(serializers.HyperlinkedModelSerializer):

    id = serializers.ReadOnlyField(source='group.id')
    name = serializers.ReadOnlyField(source='group.name')

    class Meta:
        model = Membership

        fields = ('id', 'name', 'join_date', )

for any modern implementations


回答 1

我遇到了这个问题,我的解决方案(使用DRF 3.6)是在对象上使用SerializerMethodField并显式查询Membership表,如下所示:

class MembershipSerializer(serializers.ModelSerializer):
    """Used as a nested serializer by MemberSerializer"""
    class Meta:
        model = Membership
        fields = ('id','group','join_date')

class MemberSerializer(serializers.ModelSerializer):
    groups = serializers.SerializerMethodField()

    class Meta:
        model = Member
        fields = ('id','name','groups')

    def get_groups(self, obj):
        "obj is a Member instance. Returns list of dicts"""
        qset = Membership.objects.filter(member=obj)
        return [MembershipSerializer(m).data for m in qset]

这将返回组键的字典列表,其中每个字典都从MembershipSerializer序列化。为了使其可写,您可以在MemberSerializer内定义自己的create / update方法,在其中迭代输入数据并显式创建或更新Membership模型实例。

I was facing this problem and my solution (using DRF 3.6) was to use SerializerMethodField on the object and explicitly query the Membership table like so:

class MembershipSerializer(serializers.ModelSerializer):
    """Used as a nested serializer by MemberSerializer"""
    class Meta:
        model = Membership
        fields = ('id','group','join_date')

class MemberSerializer(serializers.ModelSerializer):
    groups = serializers.SerializerMethodField()

    class Meta:
        model = Member
        fields = ('id','name','groups')

    def get_groups(self, obj):
        "obj is a Member instance. Returns list of dicts"""
        qset = Membership.objects.filter(member=obj)
        return [MembershipSerializer(m).data for m in qset]

This will return a list of dicts for the groups key where each dict is serialized from the MembershipSerializer. To make it writable, you can define your own create/update method inside the MemberSerializer where you iterate over the input data and explicitly create or update Membership model instances.


回答 2

注意:作为一名软件工程师,我喜欢使用体系结构,并且我在分层开发方法方面做过深入的工作,因此我将就层级回答它。

据我了解问题,这是解决方案models.py

class Member(models.Model):
    member_id = models.AutoField(primary_key=True)
    member_name = models.CharField(max_length = 

class Group(models.Model):
    group_id = models.AutoField(primary_key=True)
    group_name = models.CharField(max_length = 20)
    fk_member_id = models.ForeignKey('Member', models.DO_NOTHING, 
                             db_column='fk_member_id', blank=True, null=True)

class Membership(models.Model):
    membershipid = models.AutoField(primary_key=True)
    fk_group_id = models.ForeignKey('Group', models.DO_NOTHING, 
                             db_column='fk_member_id', blank=True, null=True)
    join_date = models.DateTimeField()

serializers.py

import serializer

class AllSerializer(serializer.Serializer):
    group_id = serializer.IntegerField()
    group_name = serializer.CharField(max_length = 20)
    join_date = serializer.DateTimeField()

CustomModels.py

imports...

    class AllDataModel():
        group_id = ""
        group_name = ""
        join_date = ""

BusinessLogic.py

imports ....
class getdata(memberid):
    alldataDict = {}
    dto = []
    Member = models.Members.objects.get(member_id=memberid) #or use filter for Name
    alldataDict["MemberId"] = Member.member_id
    alldataDict["MemberName"] = Member.member_name
    Groups = models.Group.objects.filter(fk_member_id=Member)
    for item in Groups:
        Custommodel = CustomModels.AllDataModel()
        Custommodel.group_id = item.group_id
        Custommodel.group_name = item.group_name
        Membership = models.Membership.objects.get(fk_group_id=item.group_id)
        Custommodel.join_date = Membership.join_date
        dto.append(Custommodel)
    serializer = AllSerializer(dto,many=True)
    alldataDict.update(serializer.data)
    return alldataDict

从技术上讲,您必须将请求传递给DataAccessLayer,该请求将从数据访问层返回过滤的对象,但是由于我必须以快速的方式回答问题,所以我在业务逻辑层中调整了代码!

NOTE: As a Software Engineer, I love to use Architectures and I have deeply worked on Layered Approach for Development so I am gonna be Answering it with Respect to Tiers.

As i understood the Issue, Here’s the Solution models.py

class Member(models.Model):
    member_id = models.AutoField(primary_key=True)
    member_name = models.CharField(max_length = 

class Group(models.Model):
    group_id = models.AutoField(primary_key=True)
    group_name = models.CharField(max_length = 20)
    fk_member_id = models.ForeignKey('Member', models.DO_NOTHING, 
                             db_column='fk_member_id', blank=True, null=True)

class Membership(models.Model):
    membershipid = models.AutoField(primary_key=True)
    fk_group_id = models.ForeignKey('Group', models.DO_NOTHING, 
                             db_column='fk_member_id', blank=True, null=True)
    join_date = models.DateTimeField()

serializers.py

import serializer

class AllSerializer(serializer.Serializer):
    group_id = serializer.IntegerField()
    group_name = serializer.CharField(max_length = 20)
    join_date = serializer.DateTimeField()

CustomModels.py

imports...

    class AllDataModel():
        group_id = ""
        group_name = ""
        join_date = ""

BusinessLogic.py

imports ....
class getdata(memberid):
    alldataDict = {}
    dto = []
    Member = models.Members.objects.get(member_id=memberid) #or use filter for Name
    alldataDict["MemberId"] = Member.member_id
    alldataDict["MemberName"] = Member.member_name
    Groups = models.Group.objects.filter(fk_member_id=Member)
    for item in Groups:
        Custommodel = CustomModels.AllDataModel()
        Custommodel.group_id = item.group_id
        Custommodel.group_name = item.group_name
        Membership = models.Membership.objects.get(fk_group_id=item.group_id)
        Custommodel.join_date = Membership.join_date
        dto.append(Custommodel)
    serializer = AllSerializer(dto,many=True)
    alldataDict.update(serializer.data)
    return alldataDict

You would technically, have to pass the Request to DataAccessLayer which would return the Filtered Objects from Data Access Layer but as I have to Answer the Question in a Fast Manner so i adjusted the Code in Business Logic Layer!


Django Rest Framework-无法使用视图名称“ user-detail”解析超链接关系的URL

问题:Django Rest Framework-无法使用视图名称“ user-detail”解析超链接关系的URL

我正在Django Rest Framework中建立一个项目,用户可以登录查看其酒窖。我的ModelViewSets工作正常,突然我收到了一个令人沮丧的错误:

无法使用视图名称“用户详细信息”解析超链接关系的URL。您可能无法在API中包含相关模型,或者lookup_field在此字段上配置了错误的属性。

追溯显示:

    [12/Dec/2013 18:35:29] "GET /bottles/ HTTP/1.1" 500 76677
Internal Server Error: /bottles/
Traceback (most recent call last):
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/django/core/handlers/base.py", line 114, in get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/rest_framework/viewsets.py", line 78, in view
    return self.dispatch(request, *args, **kwargs)
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/django/views/decorators/csrf.py", line 57, in wrapped_view
    return view_func(*args, **kwargs)
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/rest_framework/views.py", line 399, in dispatch
    response = self.handle_exception(exc)
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/rest_framework/views.py", line 396, in dispatch
    response = handler(request, *args, **kwargs)
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/rest_framework/mixins.py", line 96, in list
    return Response(serializer.data)
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/rest_framework/serializers.py", line 535, in data
    self._data = [self.to_native(item) for item in obj]
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/rest_framework/serializers.py", line 325, in to_native
    value = field.field_to_native(obj, field_name)
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/rest_framework/relations.py", line 153, in field_to_native
    return self.to_native(value)
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/rest_framework/relations.py", line 452, in to_native
    raise Exception(msg % view_name)
Exception: Could not resolve URL for hyperlinked relationship using view 
name "user-detail". You may have failed to include the related model in 
your API, or incorrectly configured the `lookup_field` attribute on this 
field.

我有一个自定义的电子邮件用户模型,并且models.py中的瓶子模型是:

class Bottle(models.Model):    
      wine = models.ForeignKey(Wine, null=False)
      user = models.ForeignKey(User, null=False, related_name='bottles')

我的序列化器:

class BottleSerializer(serializers.HyperlinkedModelSerializer):

    class Meta:
        model = Bottle
        fields = ('url', 'wine', 'user')

class UserSerializer(serializers.ModelSerializer):

    class Meta:
        model = User
        fields = ('email', 'first_name', 'last_name', 'password', 'is_superuser')

我的看法:

class BottleViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows bottles to be viewed or edited.
    """
    queryset = Bottle.objects.all()
    serializer_class = BottleSerializer

class UserViewSet(ListCreateAPIView):
    """
    API endpoint that allows users to be viewed or edited.
    """
    queryset = User.objects.all()
    serializer_class = UserSerializer

最后是URL:

router = routers.DefaultRouter()
router.register(r'bottles', views.BottleViewSet, base_name='bottles')

urlpatterns = patterns('',
    url(r'^', include(router.urls)),
    # ...

我没有用户详细信息视图,也看不到此问题可能来自何处。有任何想法吗?

谢谢

I am building a project in Django Rest Framework where users can login to view their wine cellar. My ModelViewSets were working just fine and all of a sudden I get this frustrating error:

Could not resolve URL for hyperlinked relationship using view name “user-detail”. You may have failed to include the related model in your API, or incorrectly configured the lookup_field attribute on this field.

The traceback shows:

    [12/Dec/2013 18:35:29] "GET /bottles/ HTTP/1.1" 500 76677
Internal Server Error: /bottles/
Traceback (most recent call last):
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/django/core/handlers/base.py", line 114, in get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/rest_framework/viewsets.py", line 78, in view
    return self.dispatch(request, *args, **kwargs)
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/django/views/decorators/csrf.py", line 57, in wrapped_view
    return view_func(*args, **kwargs)
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/rest_framework/views.py", line 399, in dispatch
    response = self.handle_exception(exc)
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/rest_framework/views.py", line 396, in dispatch
    response = handler(request, *args, **kwargs)
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/rest_framework/mixins.py", line 96, in list
    return Response(serializer.data)
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/rest_framework/serializers.py", line 535, in data
    self._data = [self.to_native(item) for item in obj]
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/rest_framework/serializers.py", line 325, in to_native
    value = field.field_to_native(obj, field_name)
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/rest_framework/relations.py", line 153, in field_to_native
    return self.to_native(value)
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/rest_framework/relations.py", line 452, in to_native
    raise Exception(msg % view_name)
Exception: Could not resolve URL for hyperlinked relationship using view 
name "user-detail". You may have failed to include the related model in 
your API, or incorrectly configured the `lookup_field` attribute on this 
field.

I have a custom email user model and the bottle model in models.py is:

class Bottle(models.Model):    
      wine = models.ForeignKey(Wine, null=False)
      user = models.ForeignKey(User, null=False, related_name='bottles')

My serializers:

class BottleSerializer(serializers.HyperlinkedModelSerializer):

    class Meta:
        model = Bottle
        fields = ('url', 'wine', 'user')

class UserSerializer(serializers.ModelSerializer):

    class Meta:
        model = User
        fields = ('email', 'first_name', 'last_name', 'password', 'is_superuser')

My views:

class BottleViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows bottles to be viewed or edited.
    """
    queryset = Bottle.objects.all()
    serializer_class = BottleSerializer

class UserViewSet(ListCreateAPIView):
    """
    API endpoint that allows users to be viewed or edited.
    """
    queryset = User.objects.all()
    serializer_class = UserSerializer

and finally the url:

router = routers.DefaultRouter()
router.register(r'bottles', views.BottleViewSet, base_name='bottles')

urlpatterns = patterns('',
    url(r'^', include(router.urls)),
    # ...

I don’t have a user detail view and I don’t see where this issue could come from. Any ideas?

Thanks


回答 0

由于是HyperlinkedModelSerializer序列化程序,因此您的序列化程序正在尝试解析与User上相关的URL Bottle
由于您没有用户详细信息视图,因此无法执行此操作。因此,exceptions。

  1. 不仅仅是UserViewSet在路由器上注册可以解决您的问题吗?
  2. 您可以在自己的用户字段上定义BottleSerializer以显式使用,UserSerializer而不是尝试解析URL。请参阅有关处理嵌套对象序列化器文档

Because it’s a HyperlinkedModelSerializer your serializer is trying to resolve the URL for the related User on your Bottle.
As you don’t have the user detail view it can’t do this. Hence the exception.

  1. Would not just registering the UserViewSet with the router solve your issue?
  2. You could define the user field on your BottleSerializer to explicitly use the UserSerializer rather than trying to resolve the URL. See the serializer docs on dealing with nested objects for that.

回答 1

我也遇到了此错误,并按以下方式解决了该错误:

原因是我忘记给“ **-detail”(view_name,例如:user-detail)命名空间。因此,Django Rest Framework无法找到该视图。

我的项目中有一个应用程序,假设我的项目名称为myproject,而应用程序名称为myapp

有两个urls.py文件,一个是myproject/urls.py,另一个是myapp/urls.py。我在中为应用提供了一个命名空间myproject/urls.py,就像:

url(r'', include(myapp.urls, namespace="myapp")),

我在中注册了其余框架路由器myapp/urls.py,然后出现此错误。

我的解决方案是显式地为URL提供命名空间:

class UserSerializer(serializers.HyperlinkedModelSerializer):
    url = serializers.HyperlinkedIdentityField(view_name="myapp:user-detail")

    class Meta:
        model = User
        fields = ('url', 'username')

它解决了我的问题。

I came across this error too and solved it as follows:

The reason is I forgot giving “**-detail” (view_name, e.g.: user-detail) a namespace. So, Django Rest Framework could not find that view.

There is one app in my project, suppose that my project name is myproject, and the app name is myapp.

There is two urls.py file, one is myproject/urls.py and the other is myapp/urls.py. I give the app a namespace in myproject/urls.py, just like:

url(r'', include(myapp.urls, namespace="myapp")),

I registered the rest framework routers in myapp/urls.py, and then got this error.

My solution was to provide url with namespace explicitly:

class UserSerializer(serializers.HyperlinkedModelSerializer):
    url = serializers.HyperlinkedIdentityField(view_name="myapp:user-detail")

    class Meta:
        model = User
        fields = ('url', 'username')

And it solved my problem.


回答 2

也许有人可以看看这个:http : //www.django-rest-framework.org/api-guide/routers/

如果将命名空间与超链接的序列化程序一起使用,则还需要确保序列化程序上的所有view_name参数都能正确反映命名空间。例如:

urlpatterns = [
    url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),
    url(r'^api/', include(router.urls, namespace='api')),
]

您需要包含一个参数,例如view_name='api:user-detail'超链接到用户详细信息视图的序列化程序字段的参数。

class UserSerializer(serializers.HyperlinkedModelSerializer):
    url = serializers.HyperlinkedIdentityField(view_name="api:user-detail")

class Meta:
    model = User
    fields = ('url', 'username')

Maybe someone can have a look at this : http://www.django-rest-framework.org/api-guide/routers/

If using namespacing with hyperlinked serializers you’ll also need to ensure that any view_name parameters on the serializers correctly reflect the namespace. For example:

urlpatterns = [
    url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),
    url(r'^api/', include(router.urls, namespace='api')),
]

you’d need to include a parameter such as view_name='api:user-detail' for serializer fields hyperlinked to the user detail view.

class UserSerializer(serializers.HyperlinkedModelSerializer):
    url = serializers.HyperlinkedIdentityField(view_name="api:user-detail")

class Meta:
    model = User
    fields = ('url', 'username')

回答 3

导致此错误的另一个令人讨厌的错误是在urls.py中不必要地定义了base_name。例如:

router.register(r'{pathname}', views.{ViewName}ViewSet, base_name='pathname')

这将导致上述错误。在那里获取该base_name并返回到正常工作的API。下面的代码将修复该错误。万岁!

router.register(r'{pathname}', views.{ViewName}ViewSet)

但是,您可能不仅随便添加了base_name,还因为您为View定义了自定义def get_queryset(),所以Django要求您添加base_name。在这种情况下,您需要为相关的序列化程序显式定义“ url”作为HyperlinkedIdentityField。注意,我们在抛出错误的视图的SERIALIZER上定义了此HyperlinkedIdentityField。如果我的错误是“无法使用视图名称“ study-detail”解析超链接关系的URL。您可能无法在API中包含相关模型,或者lookup_field在此字段上配置了错误的属性。” 我可以使用以下代码修复此问题。

我的ModelViewSet(自定义get_queryset是为什么我必须首先将base_name添加到router.register()的原因):

class StudyViewSet(viewsets.ModelViewSet):
    serializer_class = StudySerializer

    '''custom get_queryset'''
    def get_queryset(self):
        queryset = Study.objects.all()
        return queryset

我在urls.py中为此ModelViewSet注册的路由器:

router.register(r'studies', views.StudyViewSet, base_name='studies')

钱在哪里!然后我可以这样解决:

class StudySerializer(serializers.HyperlinkedModelSerializer):
    url = serializers.HyperlinkedIdentityField(view_name="studies-detail")
    class Meta:
        model = Study
        fields = ('url', 'name', 'active', 'created',
              'time_zone', 'user', 'surveys')

是的 您必须自己明确定义此HyperlinkedIdentityField才能起作用。而且,您需要确保view_nameHyperlinkedIdentityField上的定义与base_nameurls.py中的定义相同,并在其后添加“ -detail”。

Another nasty mistake that causes this error is having the base_name unnecessarily defined in your urls.py. For example:

router.register(r'{pathname}', views.{ViewName}ViewSet, base_name='pathname')

This will cause the error noted above. Get that base_name outta there and get back to a working API. The code below would fix the error. Hooray!

router.register(r'{pathname}', views.{ViewName}ViewSet)

However, you probably didn’t just arbitrarily add the base_name, you might have done it because you defined a custom def get_queryset() for the View and so Django mandates that you add the base_name. In this case you’ll need to explicitly define the ‘url’ as a HyperlinkedIdentityField for the serializer in question. Notice we are defining this HyperlinkedIdentityField ON THE SERIALIZER of the view that is throwing the error. If my error were “Could not resolve URL for hyperlinked relationship using view name “study-detail”. You may have failed to include the related model in your API, or incorrectly configured the lookup_field attribute on this field.” I could fix this with the following code.

My ModelViewSet (the custom get_queryset is why I had to add the base_name to the router.register() in the first place):

class StudyViewSet(viewsets.ModelViewSet):
    serializer_class = StudySerializer

    '''custom get_queryset'''
    def get_queryset(self):
        queryset = Study.objects.all()
        return queryset

My router registration for this ModelViewSet in urls.py:

router.register(r'studies', views.StudyViewSet, base_name='studies')

AND HERE’S WHERE THE MONEY IS! Then I could solve it like so:

class StudySerializer(serializers.HyperlinkedModelSerializer):
    url = serializers.HyperlinkedIdentityField(view_name="studies-detail")
    class Meta:
        model = Study
        fields = ('url', 'name', 'active', 'created',
              'time_zone', 'user', 'surveys')

Yep. You have to explicitly define this HyperlinkedIdentityField on itself for it to work. And you need to make sure that the view_name defined on the HyperlinkedIdentityField is the same as you defined on the base_name in urls.py with a ‘-detail’ added after it.


回答 4

该代码也应该起作用。

class BottleSerializer(serializers.HyperlinkedModelSerializer):

  user = UserSerializer()

  class Meta:
    model = Bottle
    fields = ('url', 'wine', 'user')

This code should work, too.

class BottleSerializer(serializers.HyperlinkedModelSerializer):

  user = UserSerializer()

  class Meta:
    model = Bottle
    fields = ('url', 'wine', 'user')

回答 5

将命名空间添加到我的网址后,我遇到了此错误

 url('api/v2/', include('api.urls', namespace='v2')),

并将app_name添加到我的urls.py

我通过在项目的settings.py中为其余框架api指定NamespaceVersioning来解决此问题

REST_FRAMEWORK = {
    'DEFAULT_VERSIONING_CLASS':'rest_framework.versioning.NamespaceVersioning'}

I ran into this error after adding namespace to my url

 url('api/v2/', include('api.urls', namespace='v2')),

and adding app_name to my urls.py

I resolved this by specifying NamespaceVersioning for my rest framework api in settings.py of my project

REST_FRAMEWORK = {
    'DEFAULT_VERSIONING_CLASS':'rest_framework.versioning.NamespaceVersioning'}

回答 6

今天,我遇到了同样的错误,下面的更改可以挽救我。

更改

class BottleSerializer(serializers.HyperlinkedModelSerializer):

至:

 class BottleSerializer(serializers.ModelSerializer):

Today, I got the same error and below changes rescue me.

Change

class BottleSerializer(serializers.HyperlinkedModelSerializer):

to:

 class BottleSerializer(serializers.ModelSerializer):

回答 7

错误相同,但原因不同:

我定义了一个自定义用户模型,没有新的字段:

from django.contrib.auth.models import (AbstractUser)
class CustomUser(AbstractUser):
    """
    custom user, reference below example
    https://github.com/jonathanchu/django-custom-user-example/blob/master/customuser/accounts/models.py

    # original User class has all I need
    # Just add __str__, not rewrite other field
    - id
    - username
    - password
    - email
    - is_active
    - date_joined
    - method, email_user
    """

    def __str__(self):
        return self.username

这是我的视图功能:

from rest_framework import permissions
from rest_framework import viewsets
from .models import (CustomUser)
class UserViewSet(viewsets.ModelViewSet):
    permission_classes = (permissions.AllowAny,)
    serializer_class = UserSerializer

    def get_queryset(self):
        queryset = CustomUser.objects.filter(id=self.request.user.id)
        if self.request.user.is_superuser:
            queryset = CustomUser.objects.all()
        return queryset

由于我没有queryset直接输入UserViewSet,因此必须base_name在注册此视图集时进行设置。这是urls.py文件引起的我的错误消息:

from myapp.views import (UserViewSet)
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'users', UserViewSet, base_name='customuser')  # <--base_name needs to be 'customuser' instead of 'user'

您需要base_name与模型名称相同- customuser

Same Error, but different reason:

I define a custom user model, nothing new field:

from django.contrib.auth.models import (AbstractUser)
class CustomUser(AbstractUser):
    """
    custom user, reference below example
    https://github.com/jonathanchu/django-custom-user-example/blob/master/customuser/accounts/models.py

    # original User class has all I need
    # Just add __str__, not rewrite other field
    - id
    - username
    - password
    - email
    - is_active
    - date_joined
    - method, email_user
    """

    def __str__(self):
        return self.username

This is my view function:

from rest_framework import permissions
from rest_framework import viewsets
from .models import (CustomUser)
class UserViewSet(viewsets.ModelViewSet):
    permission_classes = (permissions.AllowAny,)
    serializer_class = UserSerializer

    def get_queryset(self):
        queryset = CustomUser.objects.filter(id=self.request.user.id)
        if self.request.user.is_superuser:
            queryset = CustomUser.objects.all()
        return queryset

Since I didn’t give queryset directly in UserViewSet, I have to set base_name when I register this viewset. This is where my error message caused by urls.py file:

from myapp.views import (UserViewSet)
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'users', UserViewSet, base_name='customuser')  # <--base_name needs to be 'customuser' instead of 'user'

You need a base_name same as your model name – customuser.


回答 8

如果要扩展GenericViewSetListModelMixin类,并且在列表视图中添加url字段时出现相同的错误,那是因为没有定义详细信息视图。确保您正在扩展RetrieveModelMixin mixin:

class UserViewSet (mixins.ListModelMixin,
                   mixins.RetrieveModelMixin,
                   viewsets.GenericViewSet):

If you’re extending the GenericViewSet and ListModelMixin classes, and have the same error when adding the url field in the list view, it’s because you’re not defining the detail view. Be sure you’re extending the RetrieveModelMixin mixin:

class UserViewSet (mixins.ListModelMixin,
                   mixins.RetrieveModelMixin,
                   viewsets.GenericViewSet):

回答 9

似乎HyperlinkedModelSerializer不同意拥有一条道路namespace。在我的应用程序中,我做了两个更改。

# rootapp/urls.py
urlpatterns = [
    # path('api/', include('izzi.api.urls', namespace='api'))
    path('api/', include('izzi.api.urls')) # removed namespace
]

在导入的urls文件中

# app/urls.py
app_name = 'api' // removed the app_name

希望这可以帮助。

It appears that HyperlinkedModelSerializer do not agree with having a path namespace. In my application I made two changes.

# rootapp/urls.py
urlpatterns = [
    # path('api/', include('izzi.api.urls', namespace='api'))
    path('api/', include('izzi.api.urls')) # removed namespace
]

In the imported urls file

# app/urls.py
app_name = 'api' // removed the app_name

Hope this helps.


回答 10

在遵循DRF快速入门指南http://www.django-rest-framework.org/tutorial/quickstart/并尝试浏览到/ users时遇到了相同的错误 。在没有问题之前,我已经完成了多次此设置。

我的解决方案不在代码中,而是在替换数据库中。

此安装与之前的安装之间的区别是我创建本地数据库时的区别。

这次我跑了

./manage.py migrate
./manage.py createsuperuser

跑步后立即

virtualenv venv
. venv/bin/activate
pip install django
pip install djangorestframework

而不是指南中列出的确切顺序。

我怀疑数据库中未正确创建某些内容。我不在乎我的开发数据库,​​所以我删除了它并运行了./manage.py migrate再次命令,创建了一个超级用户,浏览到/ users,错误消失了。

我配置DRF和db的操作顺序出现了问题。

如果您正在使用sqlite并能够测试更改为新数据库,那么在剖析所有代码之前,值得尝试一下。

I ran into the same error while I was following the DRF quickstart guide http://www.django-rest-framework.org/tutorial/quickstart/ and then attempting to browse to /users. I’ve done this setup many times before without problems.

My solution was not in the code but in replacing the database.

The difference between this install and the others before was when I created the local database.

This time I ran my

./manage.py migrate
./manage.py createsuperuser

immediately after running

virtualenv venv
. venv/bin/activate
pip install django
pip install djangorestframework

Instead of the exact order listed in the guide.

I suspected something wasn’t properly created in the DB. I didn’t care about my dev db so I deleted it and ran the ./manage.py migrate command once more, created a super user, browsed to /users and the error was gone.

Something was problematic with the order of operations in which I configured DRF and the db.

If you are using sqlite and are able to test changing to a fresh DB then it’s worth an attempt before you go dissecting all of your code.


回答 11

Bottle =序列化器.PrimaryKeyRelatedField(read_only = True)

read_only允许您表示字段,而不必将其链接到模型的另一个视图。

Bottle = serializers.PrimaryKeyRelatedField(read_only=True)

read_only allows you to represent the field without having to link it to another view of the model.


回答 12

当数据库中的段标值为空(等于”)时,我在DRF 3.7.7上收到该错误。

I got that error on DRF 3.7.7 when a slug value was empty (equals to ”) in the database.


回答 13

我遇到了同样的问题,并通过将其generics.RetrieveAPIView作为基类添加到我的视图集来解决了该问题。

I ran into this same issue and resolved it by adding generics.RetrieveAPIView as a base class to my viewset.


回答 14

我在这个错误中停留了将近2个小时:

在/ api_users / users / 1 /上配置不正确。无法使用视图名称“ users-detail”解析超链接关系的URL。您可能没有在API中包含相关模型,或者配置了错误的lookup_field在此字段上属性。

当我终于得到解决方案但不明白为什么时,所以我的代码是:

#models.py
class Users(models.Model):
    id          = models.AutoField(primary_key=True)
    name        = models.CharField(max_length=50, blank=False, null=False)
    email       = models.EmailField(null=False, blank=False) 
    class Meta:
        verbose_name = "Usuario"
        verbose_name_plural = "Usuarios"

    def __str__(self):
        return str(self.name)


#serializers.py
class UserSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Users
        fields = (
            'id',
            'url',
            'name',        
            'email',       
            'description', 
            'active',      
            'age',         
            'some_date',   
            'timestamp',
            )
#views.py
class UserViewSet(viewsets.ModelViewSet):
    queryset = Users.objects.all()
    serializer_class = UserSerializer

#urls_api.py
router = routers.DefaultRouter()
router.register(r'users',UserViewSet, base_name='users')

urlpatterns = [ 
        url(r'^', include(router.urls)),
]

但是在我的主要网址中是:

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    #api users
    url(r'^api_users/', include('usersApi.users_urls', namespace='api')),

]

所以最后我解决了擦除命名空间的问题:

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    #api users
    url(r'^api_users/', include('usersApi.users_urls')),

]

我终于解决了我的问题,所以任何人都可以让我知道为什么,最好。

I was stuck in this error for almost 2 hours:

ImproperlyConfigured at /api_users/users/1/ Could not resolve URL for hyperlinked relationship using view name “users-detail”. You may have failed to include the related model in your API, or incorrectly configured the lookup_field attribute on this field.

When I finally get the solution but I don’t understand why, so my code is:

#models.py
class Users(models.Model):
    id          = models.AutoField(primary_key=True)
    name        = models.CharField(max_length=50, blank=False, null=False)
    email       = models.EmailField(null=False, blank=False) 
    class Meta:
        verbose_name = "Usuario"
        verbose_name_plural = "Usuarios"

    def __str__(self):
        return str(self.name)


#serializers.py
class UserSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Users
        fields = (
            'id',
            'url',
            'name',        
            'email',       
            'description', 
            'active',      
            'age',         
            'some_date',   
            'timestamp',
            )
#views.py
class UserViewSet(viewsets.ModelViewSet):
    queryset = Users.objects.all()
    serializer_class = UserSerializer

#urls_api.py
router = routers.DefaultRouter()
router.register(r'users',UserViewSet, base_name='users')

urlpatterns = [ 
        url(r'^', include(router.urls)),
]

but in my main URLs, it was:

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    #api users
    url(r'^api_users/', include('usersApi.users_urls', namespace='api')),

]

So to finally I resolve the problem erasing namespace:

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    #api users
    url(r'^api_users/', include('usersApi.users_urls')),

]

And I finally resolve my problem, so any one can let me know why, bests.


回答 15

如果您在序列化程序中省略了字段“ id”和“ url”,则不会有任何问题。无论如何,您都可以使用json对象中返回的ID来访问帖子,这使实现前端变得更加容易。

If you omit the fields ‘id’ and ‘url’ from your serializer you won’t have any problem. You can access to the posts by using the id that is returned in the json object anyways, which it makes it even easier to implement your frontend.


回答 16

我有同样的问题,我想你应该检查一下

get_absolute_url

对象模型的方法输入值(** kwargs)标题。并在lookup_field中使用确切的字段名称

I had the same problem , I think you should check your

get_absolute_url

object model’s method input value (**kwargs) title. and use exact field name in lookup_field


何时使用序列化器的create()和ModelViewset的create()perform_create()

问题:何时使用序列化器的create()和ModelViewset的create()perform_create()

我想澄清django-rest-framework有关创建模型对象的给定文档。到目前为止,我发现有3种方法来处理此类事件。

  1. 序列化器的create()方法。这是文档

    class CommentSerializer(serializers.Serializer):
    
        def create(self, validated_data):
            return Comment.objects.create(**validated_data)
    
  2. ModelViewsetcreate()方法。文献资料

    class AccountViewSet(viewsets.ModelViewSet):
    
        queryset = Account.objects.all()
        serializer_class = AccountSerializer
        permission_classes = [IsAccountAdminOrReadOnly]
    
  3. ModelViewsetperform_create()方法。文献资料

    class SnippetViewSet(viewsets.ModelViewSet):
    
        def perform_create(self, serializer):
            serializer.save(owner=self.request.user)
    

这三种方法很重要,具体取决于您的应用程序环境。

但是什么时候我们需要使用每个create() / perform_create()函数?另一方面,我发现有人要求为单个发布请求调用modelviewsetcreate()和serializer的两个create方法create()

希望任何人都可以分享他们的一些知识来进行解释,这肯定会对我的开发过程有所帮助。

I want to clarify the given documentation of django-rest-framework regarding the creation of a model object. So far I found that there are 3 approaches on how to handle such events.

  1. The Serializer’s create() method. Here is the documentation

    class CommentSerializer(serializers.Serializer):
    
        def create(self, validated_data):
            return Comment.objects.create(**validated_data)
    
  2. The ModelViewset create() method. Documentation

    class AccountViewSet(viewsets.ModelViewSet):
    
        queryset = Account.objects.all()
        serializer_class = AccountSerializer
        permission_classes = [IsAccountAdminOrReadOnly]
    
  3. The ModelViewset perform_create() method. Documentation

    class SnippetViewSet(viewsets.ModelViewSet):
    
        def perform_create(self, serializer):
            serializer.save(owner=self.request.user)
    

These three approaches are important depending on your application environment.

But WHEN do we need to use each create() / perform_create() function??. On the other hand I found some account that two create methods were called for a single post request the modelviewset’s create() and serializer’s create().

Hopefully anyone would share some of their knowledge to explain and this will surely be very helpful in my development process.


回答 0

  1. 你会使用create(self, validated_data)节约型和“刺”的价值观为就像每个模型前场添加任何额外的细节到对象**validated_data一样。理想情况下,您只想在一个位置执行这种“探测”形式,因此create您的方法CommentSerializer是最佳的选择。最重要的是,您可能还想调用外部api,以在将帐户保存到自己的数据库之前在其旁边创建用户帐户。您应该将此create功能与结合使用ModelViewSet。永远想一想-“薄视图,厚串行器”。

例:

def create(self, validated_data):
    email = validated_data.get("email", None)
    validated.pop("email") 
    # Now you have a clean valid email string 
    # You might want to call an external API or modify another table
    # (eg. keep track of number of accounts registered.) or even
    # make changes to the email format.

    # Once you are done, create the instance with the validated data
    return models.YourModel.objects.create(email=email, **validated_data)
  1. 中的create(self, request, *args, **kwargs)函数在的父类中ModelViewSet定义。的主要功能如下:CreateModelMixinModelViewSetCreateModelMixin

    from rest_framework import status
    from rest_framework.response import Response
    
    
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
    
    def perform_create(self, serializer):
        serializer.save()
    

如您所见,以上create函数负责在序列化程序上调用验证并产生正确的响应。这样做的好处是,您现在可以隔离应用程序逻辑,而不必担心平凡和重复的验证调用以及处理响应输出:)。与create(self, validated_data)序列化器(您的特定应用程序逻辑所在的位置)中的结合使用时,这可以很好地工作。

  1. 现在您可能会问,为什么我们perform_create(self, serializer)只有一行代码才有一个单独的函数!好吧,这背后的主要原因是在调用save函数时允许自定义。您可能想要在调用之前提供额外的数据save (例如serializer.save(owner=self.request.user),如果我们没有perform_create(self, serializer),那么您将不得不重写,create(self, request, *args, **kwargs)而这违背了让mixin进行繁重而乏味的工作的目的。

希望这可以帮助!

  1. You would use create(self, validated_data) to add any extra details into the object before saving AND “prod” values into each model field just like **validated_data does. Ideally speaking, you want to do this form of “prodding” only in ONE location so the create method in your CommentSerializer is the best place. On top of this, you might want to also call external apis to create user accounts on their side just before saving your accounts into your own database. You should use this create function in conjunction withModelViewSet. Always think – “Thin views, Thick serializers”.

Example:

def create(self, validated_data):
    email = validated_data.get("email", None)
    validated.pop("email") 
    # Now you have a clean valid email string 
    # You might want to call an external API or modify another table
    # (eg. keep track of number of accounts registered.) or even
    # make changes to the email format.

    # Once you are done, create the instance with the validated data
    return models.YourModel.objects.create(email=email, **validated_data)
  1. The create(self, request, *args, **kwargs) function in the ModelViewSet is defined in the CreateModelMixin class which is the parent of ModelViewSet. CreateModelMixin‘s main functions are these:

    from rest_framework import status
    from rest_framework.response import Response
    
    
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
    
    def perform_create(self, serializer):
        serializer.save()
    

As you can see, the above create function takes care of calling validation on your serializer and producing the correct response. The beauty behind this, is that you can now isolate your application logic and NOT concern yourself about the mundane and repetitive validation calls and handling response output :). This works quite well in conjuction with the create(self, validated_data) found in the serializer (where your specific application logic might reside).

  1. Now you might ask, why do we have a separate perform_create(self, serializer) function with just one line of code!?!? Well, the main reason behind this is to allow customizeability when calling the save function. You might want to supply extra data before calling save (like serializer.save(owner=self.request.user) and if we didn’t have perform_create(self, serializer), you would have to override the create(self, request, *args, **kwargs) and that just defeats the purpose of having mixins doing the heavy and boring work.

Hope this helps!


如何在Django REST Framework上启用CORS

问题:如何在Django REST Framework上启用CORS

如何在Django REST框架上启用CORS?该参考没有太大帮助,它说我可以通过中间件来完成,但是我该怎么做呢?

How can I enable CORS on my Django REST Framework? the reference doesn’t help much, it says that I can do by a middleware, but how can I do that?


回答 0

您在问题中引用的链接建议使用django-cors-headers,其文档指出要安装该库

pip install django-cors-headers

然后将其添加到已安装的应用程序中:

INSTALLED_APPS = (
    ...
    'corsheaders',
    ...
)

您还需要添加一个中间件类来侦听响应:

MIDDLEWARE_CLASSES = (
    ...
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
    ...
)

请浏览其文档的配置部分,并特别注意各种CORS_ORIGIN_设置。您需要根据需要设置其中一些。

The link you referenced in your question recommends using django-cors-headers, whose documentation says to install the library

pip install django-cors-headers

and then add it to your installed apps:

INSTALLED_APPS = (
    ...
    'corsheaders',
    ...
)

You will also need to add a middleware class to listen in on responses:

MIDDLEWARE_CLASSES = (
    ...
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
    ...
)

Please browse the configuration section of its documentation, paying particular attention to the various CORS_ORIGIN_ settings. You’ll need to set some of those based on your needs.


回答 1

pip install django-cors-headers

然后将其添加到已安装的应用程序中:

INSTALLED_APPS = (
    ...
    'corsheaders',
    ...
)

您还需要添加一个中间件类来侦听响应:

MIDDLEWARE_CLASSES = (
    ...
    'corsheaders.middleware.CorsMiddleware',  
    'django.middleware.common.CommonMiddleware',  
    ...
)

CORS_ORIGIN_ALLOW_ALL = True # If this is used then `CORS_ORIGIN_WHITELIST` will not have any effect
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_WHITELIST = [
    'http://localhost:3030',
] # If this is used, then not need to use `CORS_ORIGIN_ALLOW_ALL = True`
CORS_ORIGIN_REGEX_WHITELIST = [
    'http://localhost:3030',
]

更多详细信息:https : //github.com/ottoyiu/django-cors-headers/#configuration

阅读官方文档可以解决几乎所有问题

pip install django-cors-headers

and then add it to your installed apps:

INSTALLED_APPS = (
    ...
    'corsheaders',
    ...
)

You will also need to add a middleware class to listen in on responses:

MIDDLEWARE_CLASSES = (
    ...
    'corsheaders.middleware.CorsMiddleware',  
    'django.middleware.common.CommonMiddleware',  
    ...
)

CORS_ORIGIN_ALLOW_ALL = True # If this is used then `CORS_ORIGIN_WHITELIST` will not have any effect
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_WHITELIST = [
    'http://localhost:3030',
] # If this is used, then not need to use `CORS_ORIGIN_ALLOW_ALL = True`
CORS_ORIGIN_REGEX_WHITELIST = [
    'http://localhost:3030',
]

more details: https://github.com/ottoyiu/django-cors-headers/#configuration

read the official documentation can resolve almost all problem


回答 2

即使知道最佳选择是使用经过测试的包方法,您也可以使用自定义中间件来进行操作django-cors-headers。这样说,这里是解决方案:

创建以下结构和文件:

myapp/middleware/__init__.py

from corsMiddleware import corsMiddleware

myapp/middleware/corsMiddleware.py

class corsMiddleware(object):
    def process_response(self, req, resp):
        resp["Access-Control-Allow-Origin"] = "*"
        return resp

添加到settings.py标记的行:

MIDDLEWARE_CLASSES = (
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",

    # Now we add here our custom middleware
     'app_name.middleware.corsMiddleware' <---- this line
)

You can do by using a custom middleware, even though knowing that the best option is using the tested approach of the package django-cors-headers. With that said, here is the solution:

create the following structure and files:

myapp/middleware/__init__.py

from corsMiddleware import corsMiddleware

myapp/middleware/corsMiddleware.py

class corsMiddleware(object):
    def process_response(self, req, resp):
        resp["Access-Control-Allow-Origin"] = "*"
        return resp

add to settings.py the marked line:

MIDDLEWARE_CLASSES = (
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",

    # Now we add here our custom middleware
     'app_name.middleware.corsMiddleware' <---- this line
)

回答 3

万一有人回到这个问题并决定编写自己的中间件,这是Django新型中间件的代码示例-

class CORSMiddleware(object):
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.get_response(request)
        response["Access-Control-Allow-Origin"] = "*"

        return response

In case anyone is getting back to this question and deciding to write their own middleware, this is a code sample for Django’s new style middleware –

class CORSMiddleware(object):
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.get_response(request)
        response["Access-Control-Allow-Origin"] = "*"

        return response

回答 4

根据文档,对于Django版本> 1.10,可以将自定义MIDDLEWARE编写为函数,例如在文件中:(yourproject/middleware.py作为的同级settings.py):

def open_access_middleware(get_response):
    def middleware(request):
        response = get_response(request)
        response["Access-Control-Allow-Origin"] = "*"
        response["Access-Control-Allow-Headers"] = "*"
        return response
    return middleware

最后,将此函数的python路径(写入项目的根目录)添加到项目的MIDDLEWARE列表中settings.py

MIDDLEWARE = [
  .
  .
  'django.middleware.clickjacking.XFrameOptionsMiddleware',
  'yourproject.middleware.open_access_middleware'
]

十分简单!

For Django versions > 1.10, according to the documentation, a custom MIDDLEWARE can be written as a function, let’s say in the file: yourproject/middleware.py (as a sibling of settings.py):

def open_access_middleware(get_response):
    def middleware(request):
        response = get_response(request)
        response["Access-Control-Allow-Origin"] = "*"
        response["Access-Control-Allow-Headers"] = "*"
        return response
    return middleware

and finally, add the python path of this function (w.r.t. the root of your project) to the MIDDLEWARE list in your project’s settings.py:

MIDDLEWARE = [
  .
  .
  'django.middleware.clickjacking.XFrameOptionsMiddleware',
  'yourproject.middleware.open_access_middleware'
]

Easy peasy!


回答 5

好吧,我不认识男人,但是:

在这里使用python 3.6和django 2.2

在settings.py中将MIDDLEWARE_CLASSES重命名为MIDDLEWARE起作用。

Well, I don’t know guys but:

using here python 3.6 and django 2.2

Renaming MIDDLEWARE_CLASSES to MIDDLEWARE in settings.py worked.


回答 6

以下是不需要任何外部模块的工作步骤:

步骤1:在您的应用中创建一个模块。

例如,假设我们有一个名为user_registration_app的应用程序。探索user_registration_app并创建一个新文件。

让我们将其称为custom_cors_middleware.py

粘贴下面的类定义:

class CustomCorsMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        # One-time configuration and initialization.

    def __call__(self, request):
        # Code to be executed for each request before
        # the view (and later middleware) are called.

        response = self.get_response(request)
        response["Access-Control-Allow-Origin"] = "*"
        response["Access-Control-Allow-Headers"] = "*"

        # Code to be executed for each request/response after
        # the view is called.

        return response

步骤2:注册中间件

在您的项目settings.py文件中,添加以下行

‘user_registration_app.custom_cors_middleware.CustomCorsMiddleware’

例如:

  MIDDLEWARE = [
        'user_registration_app.custom_cors_middleware.CustomCorsMiddleware', # ADD THIS LINE BEFORE CommonMiddleware
         ...
        'django.middleware.common.CommonMiddleware',

    ]

请记住,将user_registration_app替换为在其中创建了custom_cors_middleware.py模块的应用程序的名称。

现在,您可以验证它会将必需的响应标头添加到项目中的所有视图中!

Below are the working steps without the need for any external modules:

Step 1: Create a module in your app.

E.g, lets assume we have an app called user_registration_app. Explore user_registration_app and create a new file.

Lets call this as custom_cors_middleware.py

Paste the below Class definition:

class CustomCorsMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        # One-time configuration and initialization.

    def __call__(self, request):
        # Code to be executed for each request before
        # the view (and later middleware) are called.

        response = self.get_response(request)
        response["Access-Control-Allow-Origin"] = "*"
        response["Access-Control-Allow-Headers"] = "*"

        # Code to be executed for each request/response after
        # the view is called.

        return response

Step 2: Register a middleware

In your projects settings.py file, add this line

‘user_registration_app.custom_cors_middleware.CustomCorsMiddleware’

E.g:

  MIDDLEWARE = [
        'user_registration_app.custom_cors_middleware.CustomCorsMiddleware', # ADD THIS LINE BEFORE CommonMiddleware
         ...
        'django.middleware.common.CommonMiddleware',

    ]

Remember to replace user_registration_app with the name of your app where you have created your custom_cors_middleware.py module.

You can now verify it will add the required response headers to all the views in the project!


回答 7

Django = 2.2.12 django-cors-headers = 3.2.1 djangorestframework = 3.11.0

遵循官方指示无效

最后使用旧的方法来解决。

加:

# proj/middlewares.py
from rest_framework.authentication import SessionAuthentication


class CsrfExemptSessionAuthentication(SessionAuthentication):

    def enforce_csrf(self, request):
        return  # To not perform the csrf check previously happening
#proj/settings.py

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'proj.middlewares.CsrfExemptSessionAuthentication',
    ),
}

Django=2.2.12 django-cors-headers=3.2.1 djangorestframework=3.11.0

Follow the official instruction doesn’t work

Finally use the old way to figure it out.

ADD:

# proj/middlewares.py
from rest_framework.authentication import SessionAuthentication


class CsrfExemptSessionAuthentication(SessionAuthentication):

    def enforce_csrf(self, request):
        return  # To not perform the csrf check previously happening

#proj/settings.py

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'proj.middlewares.CsrfExemptSessionAuthentication',
    ),
}

如何使用Django Rest Framework包含相关的模型字段?

问题:如何使用Django Rest Framework包含相关的模型字段?

假设我们有以下模型:

class Classroom(models.Model):
    room_number = [....]

class Teacher(models.Model):
    name = [...]
    tenure = [...]
    classroom = models.ForeignKey(Classroom)

假设不是通过ManyRelatedPrimaryKeyField函数获得这样的结果:

{
    "room_number": "42", 
    "teachers": [
        27, 
        24, 
        7
    ]
},

让它返回包含完整相关模型表示的内容,例如:

{
    "room_number": "42", 
    "teachers": [
        {
           'id':'27,
           'name':'John',
           'tenure':True
        }, 
        {
           'id':'24,
           'name':'Sally',
           'tenure':False
        }, 
    ]
},

这可能吗?如果是这样,怎么办?这是一个坏主意吗?

Let’s say that we have the following model:

class Classroom(models.Model):
    room_number = [....]

class Teacher(models.Model):
    name = [...]
    tenure = [...]
    classroom = models.ForeignKey(Classroom)

Let’s say that instead of getting a result like this per the ManyRelatedPrimaryKeyField function:

{
    "room_number": "42", 
    "teachers": [
        27, 
        24, 
        7
    ]
},

have it return something that includes the full related model representation like:

{
    "room_number": "42", 
    "teachers": [
        {
           'id':'27,
           'name':'John',
           'tenure':True
        }, 
        {
           'id':'24,
           'name':'Sally',
           'tenure':False
        }, 
    ]
},

Is this possible? If so, how? And is this a bad idea?


回答 0

最简单的方法是使用depth参数

class ClassroomSerializer(serializers.ModelSerializer):
    class Meta:
        model = Classroom
        depth = 1

但是,这仅包括前向关系的关系,在这种情况下,这并不是您真正需要的,因为教师字段是反向关系。

如果您有更复杂的要求(例如,包括反向关系,嵌套某些字段,但不嵌套其他字段,或者仅嵌套字段的特定子集),则可以嵌套序列化程序,例如…

class TeacherSerializer(serializers.ModelSerializer):
    class Meta:
        model = Teacher
        fields = ('id', 'name', 'tenure')

class ClassroomSerializer(serializers.ModelSerializer):
    teachers = TeacherSerializer(source='teacher_set')

    class Meta:
        model = Classroom

请注意,我们在序列化器字段上使用source参数来指定用作字段源的属性。我们可以通过使用模型上的related_name选项source来确保teachers属性存在,从而删除参数。Teacherclassroom = models.ForeignKey(Classroom, related_name='teachers')

要记住的一件事是,嵌套的序列化程序当前不支持写操作。对于可写表示形式,应使用常规的平面表示形式,例如pk或超链接。

The simplest way is to use the depth argument

class ClassroomSerializer(serializers.ModelSerializer):
    class Meta:
        model = Classroom
        depth = 1

However, that will only include relationships for forward relationships, which in this case isn’t quite what you need, since the teachers field is a reverse relationship.

If you’ve got more complex requirements (eg. include reverse relationships, nest some fields, but not others, or nest only a specific subset of fields) you can nest serializers, eg…

class TeacherSerializer(serializers.ModelSerializer):
    class Meta:
        model = Teacher
        fields = ('id', 'name', 'tenure')

class ClassroomSerializer(serializers.ModelSerializer):
    teachers = TeacherSerializer(source='teacher_set')

    class Meta:
        model = Classroom

Note that we use the source argument on the serializer field to specify the attribute to use as the source of the field. We could drop the source argument by instead making sure the teachers attribute exists by using the related_name option on your Teacher model, ie. classroom = models.ForeignKey(Classroom, related_name='teachers')

One thing to keep in mind is that nested serializers do not currently support write operations. For writable representations, you should use regular flat representations, such as pk or hyperlinking.


回答 1

谢谢@TomChristie !!!你帮了我很多忙!我想稍微更新一下(由于遇到错误)

class TeacherSerializer(serializers.ModelSerializer):
    class Meta:
        model = Teacher
        fields = ('id', 'name', 'tenure')

class ClassroomSerializer(serializers.ModelSerializer):
    teachers = TeacherSerializer(source='teacher_set', many=True)

    class Meta:
        model = Classroom
        field = ("teachers",)

Thank you @TomChristie!!! You helped me a lot! I would like to update that a little (because of a mistake I ran into)

class TeacherSerializer(serializers.ModelSerializer):
    class Meta:
        model = Teacher
        fields = ('id', 'name', 'tenure')

class ClassroomSerializer(serializers.ModelSerializer):
    teachers = TeacherSerializer(source='teacher_set', many=True)

    class Meta:
        model = Classroom
        field = ("teachers",)

回答 2

这也可以通过使用打包为drf-flex-fields的非常方便的dandy django来完成。我们使用它,它非常棒。您只需安装它pip install drf-flex-fields,将其通过序列化器,添加expandable_fields并宾果游戏(下面的示例)。它还允许您使用点表示法指定深层嵌套关系。

from rest_flex_fields import FlexFieldsModelSerializer

class ClassroomSerializer(FlexFieldsModelSerializer):
    class Meta:
        model = Model
        fields = ("teacher_set",)
        expandable_fields = {"teacher_set": (TeacherSerializer, {"source": "teacher_set"})}

然后,将其添加?expand=teacher_set到您的URL中,并返回扩展的响应。希望有一天能对某人有所帮助。干杯!

This can also be accomplished by using a pretty handy dandy django packaged called drf-flex-fields. We use it and it’s pretty awesome. You just install it pip install drf-flex-fields, pass it through your serializer, add expandable_fields and bingo (example below). It also allows you to specify deep nested relationships by using dot notation.

from rest_flex_fields import FlexFieldsModelSerializer

class ClassroomSerializer(FlexFieldsModelSerializer):
    class Meta:
        model = Model
        fields = ("teacher_set",)
        expandable_fields = {"teacher_set": (TeacherSerializer, {"source": "teacher_set"})}

Then you add ?expand=teacher_set to your URL and it returns an expanded response. Hope this helps someone, someday. Cheers!