Django Rest Framework - Không thể phân giải URL cho mối quan hệ siêu liên kết bằng cách sử dụng tên chế độ xem “user-detail”


108

Tôi đang xây dựng một dự án trong Django Rest Framework nơi người dùng có thể đăng nhập để xem hầm rượu của họ. ModelViewSets của tôi hoạt động tốt và đột nhiên tôi gặp lỗi khó chịu này:

Không thể phân giải URL cho mối quan hệ siêu liên kết bằng cách sử dụng tên chế độ xem "user-detail". Bạn có thể đã không đưa mô hình liên quan vào API của mình hoặc định cấu hình sai lookup_fieldthuộc tính trên trường này.

Việc truy tìm lại cho thấy:

    [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.

Tôi có một mô hình người dùng email tùy chỉnh và mô hình chai trong models.py là:

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

Bộ tuần tự của tôi:

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

Quan điểm của tôi:

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

và cuối cùng là url:

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

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

Tôi không có chế độ xem chi tiết người dùng và tôi không biết vấn đề này có thể đến từ đâu. Bất kỳ ý tưởng?

Cảm ơn

Câu trả lời:


96

Bởi vì đó là bộ HyperlinkedModelSerializertuần tự của bạn đang cố gắng giải quyết URL cho liên quan Usertrên của bạn Bottle.
Vì bạn không có chế độ xem chi tiết người dùng, nó không thể làm điều này. Do đó ngoại lệ.

  1. Không chỉ đăng ký UserViewSetvới bộ định tuyến có giải quyết được vấn đề của bạn không?
  2. Bạn có thể xác định trường người dùng trên của mình BottleSerializerđể sử dụng một cách rõ ràng UserSerializerthay vì cố gắng phân giải URL. Xem tài liệu về bộ tuần tự về cách xử lý các đối tượng lồng nhau cho điều đó .

1
Rất cảm ơn, tôi đã nhận xét UserViewSet trong bộ định tuyến của mình, điều đó đã giải quyết được nó!
bpipat

5
ĐÓ LÀ ĐIỂM ---- làm điều đó một cách rõ ràng --- đối với nhiều điều kỳ diệu là mất nhiều thời gian.
andilabs

Bạn có thể vui lòng chỉ ra những gì được cấu hình sai trong dự án của tôi không?
JJD

@ GrijeshChauhan— Cảm ơn! Hiện đã được sửa.
Carlton Gibson

Lý do nó không hoạt động là vì django muốn hiển thị dữ liệu liên quan từ Người dùng trong chế độ xem hiện tại của bạn cho Người dùng tham số. Thông thường, nó chọn một danh sách các giá trị có sẵn. Vì UserViewSet không được xác định nên không thể kéo các chi tiết để hiển thị trang web. Thêm UserViewSet và thiết lập lại bên dưới bộ định tuyến mặc định sẽ làm cho mọi thứ trở nên hoàn chỉnh để hiển thị tất cả các thành phần.
Doogle

65

Tôi cũng đã gặp lỗi này và giải quyết nó như sau:

Lý do là tôi quên không cho "** - detail" (view_name, ví dụ: user-detail) một vùng tên. Vì vậy, Django Rest Framework không thể tìm thấy dạng xem đó.

Có một ứng dụng trong dự án của tôi, giả sử rằng tên dự án của tôi là myprojectvà tên ứng dụng là myapp.

Có hai tệp urls.py, một là tệp và tệp myproject/urls.pykia là myapp/urls.py. Tôi cấp cho ứng dụng một không gian tên myproject/urls.py, giống như:

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

Tôi đã đăng ký các bộ định tuyến khung phần còn lại myapp/urls.py, và sau đó gặp lỗi này.

Giải pháp của tôi là cung cấp url với không gian tên một cách rõ ràng:

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

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

Và nó đã giải quyết được vấn đề của tôi.


@boveson, điều này hoạt động như một sự quyến rũ! Cảm ơn bạn đã giải quyết hàng giờ thất vọng về phía tôi.
lmiguelvargasf

Điều này cũng làm cho nó hoạt động cho tôi. Một điểm quan trọng nữa về phía tôi là cách viết chính xác của base_name trong Route!
maggie

1
Chìa khóa ở đây là tiền tố không gian tên ngăn đảo ngược hoạt động .....
boatcoder

Tôi đã gặp sự cố như thế này và câu trả lời này đã khắc phục sự cố của tôi sau 3 giờ tìm kiếm! @bovenson
Whale 52Hz, 29/07/19

hoặc bạn có thể sử dụng extra_kwargs như drf đề xuất:extra_kwargs = {'url': {'view_name': 'myapp:user-detail'}}
ChrisRob

19

Có thể ai đó có thể xem cái này: http://www.django-rest-framework.org/api-guide/routers/

Nếu sử dụng không gian tên với bộ tuần tự hóa siêu liên kết, bạn cũng cần đảm bảo rằng bất kỳ thông số view_name nào trên bộ tuần tự phản ánh đúng không gian tên. Ví dụ:

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

bạn cần bao gồm một tham số như view_name='api:user-detail'cho các trường của bộ tuần tự được siêu liên kết với chế độ xem chi tiết người dùng.

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

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

1
Tóm lại, cho api của bạn một không gian tên gây ra lỗi trong tiêu đề, bạn có thể không muốn làm điều đó trừ khi bạn muốn thay đổi nó ở nhiều nơi.
Đánh dấu

đã làm cho tôi! tôi urls.pyđã được đôi lồng trong trong tôi newsitedự án: (1) newsite/urls.py(được tạo ra bởi django) (2) polls/urls.py(3) polls/api/v1/urls.py ............ tôi phải đề cập đến tên lồng nhau sử dụngurl = serializers.HyperlinkedIdentityField(view_name="polls:polls_api:user-detail")
Grijesh Chauhan

12

Một lỗi khó chịu khác gây ra lỗi này là việc base_name được xác định một cách không cần thiết trong urls.py. Ví dụ:

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

Điều này sẽ gây ra lỗi được lưu ý ở trên. Lấy base_name đó ra khỏi đó và quay lại API đang hoạt động. Đoạn mã dưới đây sẽ sửa lỗi. Hoan hô!

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

Tuy nhiên, có thể bạn không chỉ tự ý thêm base_name, bạn có thể đã làm điều đó vì bạn đã xác định một get_queryset () def tùy chỉnh cho View và do đó Django bắt buộc bạn phải thêm base_name. Trong trường hợp này, bạn sẽ cần xác định rõ ràng 'url' là HyperlinkedIdentityField cho bộ tuần tự được đề cập. Lưu ý rằng chúng tôi đang xác định HyperlinkedIdentityField này TRÊN SERIALIZER của chế độ xem đang gây ra lỗi. Nếu lỗi của tôi là "Không thể giải quyết URL cho mối quan hệ siêu liên kết bằng cách sử dụng tên chế độ xem" study-detail ". Có thể bạn đã không đưa mô hình liên quan vào API của mình hoặc định cấu hình sai lookup_fieldthuộc tính trên trường này." Tôi có thể sửa lỗi này bằng mã sau.

ModelViewSet của tôi (get_queryset tùy chỉnh là lý do tại sao tôi phải thêm base_name vào router.register () ngay từ đầu):

class StudyViewSet(viewsets.ModelViewSet):
    serializer_class = StudySerializer

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

Đăng ký bộ định tuyến của tôi cho ModelViewSet này trong urls.py:

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

VÀ ĐÂY LÀ TIỀN Ở ĐÂU! Sau đó, tôi có thể giải quyết nó như vậy:

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

Vâng. Bạn phải xác định rõ ràng HyperlinkedIdentityField này để nó hoạt động. Và bạn cần đảm bảo rằng view_nameđịnh nghĩa trên HyperlinkedIdentityField giống như bạn đã xác định trên base_nametrong urls.py với '-detail' được thêm vào sau nó.


2
Điều này làm việc cho tôi, tuy nhiên tôi phải đặt toàn bộ lộ trình <app_name>:studies-detail. Ví dụ: nếu ứng dụng của tôi được gọi tanks, thì đường dẫn đầy đủ sẽ là HyperlinkedIdentityField(view_name="tanks:studies-detail"). Để tìm ra điều đó, tôi đã sử dụng lệnh django-exensions show_urls , để xem toàn bộ tuyến đường và nhãn mà bộ định tuyến tự động tạo.
dtasev 30/03/18

10

Mã này cũng sẽ hoạt động.

class BottleSerializer(serializers.HyperlinkedModelSerializer):

  user = UserSerializer()

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

3
Đáng chú ý là UserSerializerphải được thực hiện (nó không phải là sẵn sàng để nhập khẩu), như trong django-rest-framework.org/api-guide/serializers
Caumons

Điều này đã hiệu quả với tôi, nhưng để nó hoạt động, tôi phải thay đổi router.register (r'bottles ', views.BottleViewSet, base_name =' Bottle ') thành router.register (r'bottles', views.BottleViewSet). Tôi không biết tại sao cần phải có sự thay đổi này.
manpikin 25/09/19

4

Tôi gặp phải lỗi này sau khi thêm không gian tên vào url của mình

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

và thêm app_name vào urls.py của tôi

Tôi đã giải quyết vấn đề này bằng cách chỉ định NamespaceVersions cho api khung còn lại của mình trong settings.py dự án của tôi

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

3

Hôm nay, tôi gặp lỗi tương tự và các thay đổi dưới đây giải cứu tôi.

Thay đổi

class BottleSerializer(serializers.HyperlinkedModelSerializer):

đến:

 class BottleSerializer(serializers.ModelSerializer):

2

Cùng một lỗi, nhưng lý do khác nhau:

Tôi xác định một mô hình người dùng tùy chỉnh, không có trường nào mới:

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

Đây là chức năng xem của tôi:

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

Vì tôi không tham gia querysettrực tiếp UserViewSetnên tôi phải thiết lập base_namekhi đăng ký bộ chế độ xem này. Đây là nơi thông báo lỗi của tôi do urls.pytệp gây ra :

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'

Bạn cần base_namegiống như tên kiểu máy của bạn - customuser.


Bài cũ, nhưng nhận xét của bạn "# <- base_name cần phải là" customuser "thay vì" user "" là những gì đã lưu trong ngày của tôi. Cảm ơn!
Hannon César

1

Nếu bạn đang mở rộng các lớp GenericViewSetListModelMixin và gặp phải lỗi tương tự khi thêm trường url trong chế độ xem danh sách, đó là do bạn không xác định chế độ xem chi tiết. Hãy chắc chắn rằng bạn đang mở rộng hỗn hợp RetrieveModelMixin :

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

1

Có vẻ HyperlinkedModelSerializernhư không đồng ý với việc có một con đường namespace. Trong đơn đăng ký của tôi, tôi đã thực hiện hai thay đổi.

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

Trong tệp url đã nhập

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

Hi vọng điêu nay co ich.


0

Tôi đã gặp phải lỗi tương tự khi đang làm theo hướng dẫn bắt đầu nhanh DRF http://www.django-rest-framework.org/tutorial/quickstart/ và sau đó cố gắng duyệt tới / users. Tôi đã thực hiện thiết lập này nhiều lần trước đây mà không gặp sự cố.

Giải pháp của tôi không nằm trong mã mà là thay thế cơ sở dữ liệu.

Sự khác biệt giữa cài đặt này và những cài đặt khác trước đây là khi tôi tạo cơ sở dữ liệu cục bộ.

Lần này tôi chạy

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

ngay sau khi chạy

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

Thay vì thứ tự chính xác được liệt kê trong hướng dẫn.

Tôi nghi ngờ có điều gì đó không được tạo đúng cách trong DB. Tôi không quan tâm đến db nhà phát triển của mình nên tôi đã xóa nó và chạy./manage.py migrate lệnh một lần nữa, tạo siêu người dùng, duyệt cho / người dùng và lỗi đã biến mất.

Có vấn đề gì đó với thứ tự hoạt động mà tôi đã định cấu hình DRF và db.

Nếu bạn đang sử dụng sqlite và có thể thử nghiệm thay đổi sang DB mới thì bạn nên thử trước khi mổ xẻ tất cả mã của mình.


0

Chai = serializers.PrimaryKeyRelatedField (read_only = True)

read_only cho phép bạn biểu diễn trường mà không cần phải liên kết trường đó với một dạng xem khác của mô hình.


0

Tôi gặp lỗi đó trên DRF 3.7.7 khi giá trị slug trống (bằng '') trong cơ sở dữ liệu.


0

Tôi đã gặp phải vấn đề tương tự này và đã giải quyết nó bằng cách thêm generics.RetrieveAPIViewlàm lớp cơ sở vào tập quan sát của mình.


0

Tôi đã mắc phải lỗi này trong gần 2 giờ:

Được định cấu hình không đúng tại / api_users / users / 1 / Không thể phân giải URL cho mối quan hệ siêu liên kết bằng cách sử dụng tên chế độ xem "users-detail". Bạn có thể đã không đưa mô hình liên quan vào API của mình hoặc định cấu hình sai lookup_fieldthuộc tính trên trường này.

Cuối cùng khi tôi nhận được giải pháp nhưng tôi không hiểu tại sao, vì vậy mã của tôi là:

#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)),
]

nhưng trong các URL chính của tôi, nó là:

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

]

Vì vậy, cuối cùng tôi giải quyết vấn đề xóa không gian tên:

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

]

Và cuối cùng tôi đã giải quyết được vấn đề của mình, vì vậy bất kỳ ai cũng có thể cho tôi biết lý do tại sao, tốt nhất.


0

Nếu bạn bỏ qua các trường 'id' và 'url' khỏi bộ tuần tự, bạn sẽ không gặp vấn đề gì. Bạn có thể truy cập vào các bài đăng bằng cách sử dụng id được trả lại trong đối tượng json, điều này giúp việc triển khai giao diện người dùng của bạn thậm chí còn dễ dàng hơn.


0

Tôi đã gặp vấn đề tương tự, tôi nghĩ bạn nên kiểm tra

get_absolute_url

tiêu đề giá trị đầu vào phương thức của mô hình đối tượng (** kwargs). và sử dụng tên trường chính xác trong lookup_field

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.